From ec70f70b6e653c71ce062faf0bcb1379ea81a907 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 15 Jul 2020 18:35:39 -0700 Subject: [PATCH 001/151] "Hello World" file_picker plugin --- packages/file_picker/file_picker/CHANGELOG.md | 3 + packages/file_picker/file_picker/LICENSE | 27 ++++++ packages/file_picker/file_picker/README.md | 3 + .../file_picker/example/.gitignore | 48 +++++++++++ .../file_picker/file_picker/example/.metadata | 10 +++ .../file_picker/file_picker/example/README.md | 16 ++++ .../file_picker/example/lib/main.dart | 71 ++++++++++++++++ .../file_picker/example/pubspec.yaml | 79 ++++++++++++++++++ .../file_picker/example/test/widget_test.dart | 30 +++++++ .../file_picker/example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../file_picker/example/web/index.html | 33 ++++++++ .../file_picker/example/web/manifest.json | 23 +++++ .../file_picker/lib/file_picker.dart | 14 ++++ packages/file_picker/file_picker/pubspec.yaml | 32 +++++++ .../file_picker/test/file_picker_test.dart | 8 ++ .../CHANGELOG.md | 3 + .../file_picker_platform_interface/LICENSE | 27 ++++++ .../file_picker_platform_interface/README.md | 26 ++++++ .../lib/file_picker_platform_interface.dart | 43 ++++++++++ .../lib/method_channel_file_picker.dart | 21 +++++ .../pubspec.yaml | 22 +++++ .../test/method_channel_file_picker.dart | 14 ++++ .../file_picker/file_picker_web/CHANGELOG.md | 3 + packages/file_picker/file_picker_web/LICENSE | 27 ++++++ .../file_picker/file_picker_web/README.md | 37 ++++++++ .../file_picker_web/lib/file_picker_web.dart | 20 +++++ .../file_picker/file_picker_web/pubspec.yaml | 35 ++++++++ .../test/file_picker_web_test.dart | 17 ++++ 30 files changed, 692 insertions(+) create mode 100644 packages/file_picker/file_picker/CHANGELOG.md create mode 100644 packages/file_picker/file_picker/LICENSE create mode 100644 packages/file_picker/file_picker/README.md create mode 100644 packages/file_picker/file_picker/example/.gitignore create mode 100644 packages/file_picker/file_picker/example/.metadata create mode 100644 packages/file_picker/file_picker/example/README.md create mode 100644 packages/file_picker/file_picker/example/lib/main.dart create mode 100644 packages/file_picker/file_picker/example/pubspec.yaml create mode 100644 packages/file_picker/file_picker/example/test/widget_test.dart create mode 100644 packages/file_picker/file_picker/example/web/favicon.png create mode 100644 packages/file_picker/file_picker/example/web/icons/Icon-192.png create mode 100644 packages/file_picker/file_picker/example/web/icons/Icon-512.png create mode 100644 packages/file_picker/file_picker/example/web/index.html create mode 100644 packages/file_picker/file_picker/example/web/manifest.json create mode 100644 packages/file_picker/file_picker/lib/file_picker.dart create mode 100644 packages/file_picker/file_picker/pubspec.yaml create mode 100644 packages/file_picker/file_picker/test/file_picker_test.dart create mode 100644 packages/file_picker/file_picker_platform_interface/CHANGELOG.md create mode 100644 packages/file_picker/file_picker_platform_interface/LICENSE create mode 100644 packages/file_picker/file_picker_platform_interface/README.md create mode 100644 packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/method_channel_file_picker.dart create mode 100644 packages/file_picker/file_picker_platform_interface/pubspec.yaml create mode 100644 packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart create mode 100644 packages/file_picker/file_picker_web/CHANGELOG.md create mode 100644 packages/file_picker/file_picker_web/LICENSE create mode 100644 packages/file_picker/file_picker_web/README.md create mode 100644 packages/file_picker/file_picker_web/lib/file_picker_web.dart create mode 100644 packages/file_picker/file_picker_web/pubspec.yaml create mode 100644 packages/file_picker/file_picker_web/test/file_picker_web_test.dart diff --git a/packages/file_picker/file_picker/CHANGELOG.md b/packages/file_picker/file_picker/CHANGELOG.md new file mode 100644 index 000000000000..5703fc8bcbb3 --- /dev/null +++ b/packages/file_picker/file_picker/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.1.0 + +* Initial Open Source release. diff --git a/packages/file_picker/file_picker/LICENSE b/packages/file_picker/file_picker/LICENSE new file mode 100644 index 000000000000..000b4618d2bd --- /dev/null +++ b/packages/file_picker/file_picker/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/packages/file_picker/file_picker/README.md b/packages/file_picker/file_picker/README.md new file mode 100644 index 000000000000..670bdf2012a1 --- /dev/null +++ b/packages/file_picker/file_picker/README.md @@ -0,0 +1,3 @@ +# file_picker + +A Flutter plugin that currently doesn't do anything. diff --git a/packages/file_picker/file_picker/example/.gitignore b/packages/file_picker/file_picker/example/.gitignore new file mode 100644 index 000000000000..7abd0753cfc3 --- /dev/null +++ b/packages/file_picker/file_picker/example/.gitignore @@ -0,0 +1,48 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Currently only web supported +android/ +ios/ + +# Exceptions to above rules. +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/packages/file_picker/file_picker/example/.metadata b/packages/file_picker/file_picker/example/.metadata new file mode 100644 index 000000000000..897381f2373f --- /dev/null +++ b/packages/file_picker/file_picker/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 7736f3bc90270dcb0480db2ccffbf1d13c28db85 + channel: dev + +project_type: app diff --git a/packages/file_picker/file_picker/example/README.md b/packages/file_picker/file_picker/example/README.md new file mode 100644 index 000000000000..a13562602822 --- /dev/null +++ b/packages/file_picker/file_picker/example/README.md @@ -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. diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart new file mode 100644 index 000000000000..00edac95ff8f --- /dev/null +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:file_picker/file_picker.dart'; + +void main() { + runApp(MyApp()); +} + +/// MyApp is the Main Application +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: MyHomePage(title: 'File Picker Demo Home Page'), + ); + } +} + +/// Home Page of the application +class MyHomePage extends StatefulWidget { + + /// Constructor for MyHomePage + MyHomePage({Key key, this.title}) : super(key: key); + + /// Title of Home Page + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +/// State of Home Page +class _MyHomePageState extends State { + String _msg = ""; + + void _getMessage() async { + String msg = await getMessage(); + setState(() { + this._msg = "Here is your message: " + msg; + }); + } + + @override + Widget build(BuildContext context) { + + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + this._msg + ), + SizedBox(height: 10), + RaisedButton( + child: Text('Press for a message'), + onPressed: () => { _getMessage() }, + ), + ], + ), + ), + ); + } +} diff --git a/packages/file_picker/file_picker/example/pubspec.yaml b/packages/file_picker/file_picker/example/pubspec.yaml new file mode 100644 index 000000000000..baa9292c63cc --- /dev/null +++ b/packages/file_picker/file_picker/example/pubspec.yaml @@ -0,0 +1,79 @@ +name: example +description: A new Flutter project. + +# The following line prevents the package from being accidentally published to +# pub.dev using `pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=2.7.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + + file_picker: + path: ../ + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^0.1.3 + +dev_dependencies: + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, 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 from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/file_picker/file_picker/example/test/widget_test.dart b/packages/file_picker/file_picker/example/test/widget_test.dart new file mode 100644 index 000000000000..747db1da35e8 --- /dev/null +++ b/packages/file_picker/file_picker/example/test/widget_test.dart @@ -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); + }); +} diff --git a/packages/file_picker/file_picker/example/web/favicon.png b/packages/file_picker/file_picker/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/packages/file_picker/file_picker/example/web/icons/Icon-192.png b/packages/file_picker/file_picker/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 zcmZ`-2T+sGz6~)*FVZ`aW+(v>MIm&M-g^@e2u-B-DoB?qO+b1Tq<5uCCv>ESfRum& zp%X;f!~1{tzL__3=gjVJ=j=J>+nMj%ncXj1Q(b|Ckbw{Y0FWpt%4y%$uD=Z*c-x~o zE;IoE;xa#7Ll5nj-e4CuXB&G*IM~D21rCP$*xLXAK8rIMCSHuSu%bL&S3)8YI~vyp@KBu9Ph7R_pvKQ@xv>NQ`dZp(u{Z8K3yOB zn7-AR+d2JkW)KiGx0hosml;+eCXp6+w%@STjFY*CJ?udJ64&{BCbuebcuH;}(($@@ znNlgBA@ZXB)mcl9nbX#F!f_5Z=W>0kh|UVWnf!At4V*LQP%*gPdCXd6P@J4Td;!Ur z<2ZLmwr(NG`u#gDEMP19UcSzRTL@HsK+PnIXbVBT@oHm53DZr?~V(0{rsalAfwgo zEh=GviaqkF;}F_5-yA!1u3!gxaR&Mj)hLuj5Q-N-@Lra{%<4ONja8pycD90&>yMB` zchhd>0CsH`^|&TstH-8+R`CfoWqmTTF_0?zDOY`E`b)cVi!$4xA@oO;SyOjJyP^_j zx^@Gdf+w|FW@DMdOi8=4+LJl$#@R&&=UM`)G!y%6ZzQLoSL%*KE8IO0~&5XYR9 z&N)?goEiWA(YoRfT{06&D6Yuu@Qt&XVbuW@COb;>SP9~aRc+z`m`80pB2o%`#{xD@ zI3RAlukL5L>px6b?QW1Ac_0>ew%NM!XB2(H+1Y3AJC?C?O`GGs`331Nd4ZvG~bMo{lh~GeL zSL|tT*fF-HXxXYtfu5z+T5Mx9OdP7J4g%@oeC2FaWO1D{=NvL|DNZ}GO?O3`+H*SI z=grGv=7dL{+oY0eJFGO!Qe(e2F?CHW(i!!XkGo2tUvsQ)I9ev`H&=;`N%Z{L zO?vV%rDv$y(@1Yj@xfr7Kzr<~0{^T8wM80xf7IGQF_S-2c0)0D6b0~yD7BsCy+(zL z#N~%&e4iAwi4F$&dI7x6cE|B{f@lY5epaDh=2-(4N05VO~A zQT3hanGy_&p+7Fb^I#ewGsjyCEUmSCaP6JDB*=_()FgQ(-pZ28-{qx~2foO4%pM9e z*_63RT8XjgiaWY|*xydf;8MKLd{HnfZ2kM%iq}fstImB-K6A79B~YoPVa@tYN@T_$ zea+9)<%?=Fl!kd(Y!G(-o}ko28hg2!MR-o5BEa_72uj7Mrc&{lRh3u2%Y=Xk9^-qa zBPWaD=2qcuJ&@Tf6ue&)4_V*45=zWk@Z}Q?f5)*z)-+E|-yC4fs5CE6L_PH3=zI8p z*Z3!it{1e5_^(sF*v=0{`U9C741&lub89gdhKp|Y8CeC{_{wYK-LSbp{h)b~9^j!s z7e?Y{Z3pZv0J)(VL=g>l;<}xk=T*O5YR|hg0eg4u98f2IrA-MY+StQIuK-(*J6TRR z|IM(%uI~?`wsfyO6Tgmsy1b3a)j6M&-jgUjVg+mP*oTKdHg?5E`!r`7AE_#?Fc)&a z08KCq>Gc=ne{PCbRvs6gVW|tKdcE1#7C4e`M|j$C5EYZ~Y=jUtc zj`+?p4ba3uy7><7wIokM79jPza``{Lx0)zGWg;FW1^NKY+GpEi=rHJ+fVRGfXO zPHV52k?jxei_!YYAw1HIz}y8ZMwdZqU%ESwMn7~t zdI5%B;U7RF=jzRz^NuY9nM)&<%M>x>0(e$GpU9th%rHiZsIT>_qp%V~ILlyt^V`=d z!1+DX@ah?RnB$X!0xpTA0}lN@9V-ePx>wQ?-xrJr^qDlw?#O(RsXeAvM%}rg0NT#t z!CsT;-vB=B87ShG`GwO;OEbeL;a}LIu=&@9cb~Rsx(ZPNQ!NT7H{@j0e(DiLea>QD zPmpe90gEKHEZ8oQ@6%E7k-Ptn#z)b9NbD@_GTxEhbS+}Bb74WUaRy{w;E|MgDAvHw zL)ycgM7mB?XVh^OzbC?LKFMotw3r@i&VdUV%^Efdib)3@soX%vWCbnOyt@Y4swW925@bt45y0HY3YI~BnnzZYrinFy;L?2D3BAL`UQ zEj))+f>H7~g8*VuWQ83EtGcx`hun$QvuurSMg3l4IP8Fe`#C|N6mbYJ=n;+}EQm;< z!!N=5j1aAr_uEnnzrEV%_E|JpTb#1p1*}5!Ce!R@d$EtMR~%9# zd;h8=QGT)KMW2IKu_fA_>p_und#-;Q)p%%l0XZOXQicfX8M~7?8}@U^ihu;mizj)t zgV7wk%n-UOb z#!P5q?Ex+*Kx@*p`o$q8FWL*E^$&1*!gpv?Za$YO~{BHeGY*5%4HXUKa_A~~^d z=E*gf6&+LFF^`j4$T~dR)%{I)T?>@Ma?D!gi9I^HqvjPc3-v~=qpX1Mne@*rzT&Xw zQ9DXsSV@PqpEJO-g4A&L{F&;K6W60D!_vs?Vx!?w27XbEuJJP&);)^+VF1nHqHBWu z^>kI$M9yfOY8~|hZ9WB!q-9u&mKhEcRjlf2nm_@s;0D#c|@ED7NZE% zzR;>P5B{o4fzlfsn3CkBK&`OSb-YNrqx@N#4CK!>bQ(V(D#9|l!e9(%sz~PYk@8zt zPN9oK78&-IL_F zhsk1$6p;GqFbtB^ZHHP+cjMvA0(LqlskbdYE_rda>gvQLTiqOQ1~*7lg%z*&p`Ry& zRcG^DbbPj_jOKHTr8uk^15Boj6>hA2S-QY(W-6!FIq8h$<>MI>PYYRenQDBamO#Fv zAH5&ImqKBDn0v5kb|8i0wFhUBJTpT!rB-`zK)^SNnRmLraZcPYK7b{I@+}wXVdW-{Ps17qdRA3JatEd?rPV z4@}(DAMf5EqXCr4-B+~H1P#;t@O}B)tIJ(W6$LrK&0plTmnPpb1TKn3?f?Kk``?D+ zQ!MFqOX7JbsXfQrz`-M@hq7xlfNz;_B{^wbpG8des56x(Q)H)5eLeDwCrVR}hzr~= zM{yXR6IM?kXxauLza#@#u?Y|o;904HCqF<8yT~~c-xyRc0-vxofnxG^(x%>bj5r}N zyFT+xnn-?B`ohA>{+ZZQem=*Xpqz{=j8i2TAC#x-m;;mo{{sLB_z(UoAqD=A#*juZ zCv=J~i*O8;F}A^Wf#+zx;~3B{57xtoxC&j^ie^?**T`WT2OPRtC`xj~+3Kprn=rVM zVJ|h5ux%S{dO}!mq93}P+h36mZ5aZg1-?vhL$ke1d52qIiXSE(llCr5i=QUS?LIjc zV$4q=-)aaR4wsrQv}^shL5u%6;`uiSEs<1nG^?$kl$^6DL z43CjY`M*p}ew}}3rXc7Xck@k41jx}c;NgEIhKZ*jsBRZUP-x2cm;F1<5$jefl|ppO zmZd%%?gMJ^g9=RZ^#8Mf5aWNVhjAS^|DQO+q$)oeob_&ZLFL(zur$)); zU19yRm)z<4&4-M}7!9+^Wl}Uk?`S$#V2%pQ*SIH5KI-mn%i;Z7-)m$mN9CnI$G7?# zo`zVrUwoSL&_dJ92YhX5TKqaRkfPgC4=Q&=K+;_aDs&OU0&{WFH}kKX6uNQC6%oUH z2DZa1s3%Vtk|bglbxep-w)PbFG!J17`<$g8lVhqD2w;Z0zGsh-r zxZ13G$G<48leNqR!DCVt9)@}(zMI5w6Wo=N zpP1*3DI;~h2WDWgcKn*f!+ORD)f$DZFwgKBafEZmeXQMAsq9sxP9A)7zOYnkHT9JU zRA`umgmP9d6=PHmFIgx=0$(sjb>+0CHG)K@cPG{IxaJ&Ueo8)0RWgV9+gO7+Bl1(F z7!BslJ2MP*PWJ;x)QXbR$6jEr5q3 z(3}F@YO_P1NyTdEXRLU6fp?9V2-S=E+YaeLL{Y)W%6`k7$(EW8EZSA*(+;e5@jgD^I zaJQ2|oCM1n!A&-8`;#RDcZyk*+RPkn_r8?Ak@agHiSp*qFNX)&i21HE?yuZ;-C<3C zwJGd1lx5UzViP7sZJ&|LqH*mryb}y|%AOw+v)yc`qM)03qyyrqhX?ub`Cjwx2PrR! z)_z>5*!*$x1=Qa-0uE7jy0z`>|Ni#X+uV|%_81F7)b+nf%iz=`fF4g5UfHS_?PHbr zB;0$bK@=di?f`dS(j{l3-tSCfp~zUuva+=EWxJcRfp(<$@vd(GigM&~vaYZ0c#BTs z3ijkxMl=vw5AS&DcXQ%eeKt!uKvh2l3W?&3=dBHU=Gz?O!40S&&~ei2vg**c$o;i89~6DVns zG>9a*`k5)NI9|?W!@9>rzJ;9EJ=YlJTx1r1BA?H`LWijk(rTax9(OAu;q4_wTj-yj z1%W4GW&K4T=uEGb+E!>W0SD_C0RR91 literal 0 HcmV?d00001 diff --git a/packages/file_picker/file_picker/example/web/icons/Icon-512.png b/packages/file_picker/file_picker/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 zcmd5=2T+s!lYZ%-(h(2@5fr2dC?F^$C=i-}R6$UX8af(!je;W5yC_|HmujSgN*6?W z3knF*TL1$|?oD*=zPbBVex*RUIKsL<(&Rj9%^UD2IK3W?2j>D?eWQgvS-HLymHo9%~|N2Q{~j za?*X-{b9JRowv_*Mh|;*-kPFn>PI;r<#kFaxFqbn?aq|PduQg=2Q;~Qc}#z)_T%x9 zE|0!a70`58wjREmAH38H1)#gof)U3g9FZ^ zF7&-0^Hy{4XHWLoC*hOG(dg~2g6&?-wqcpf{ z&3=o8vw7lMi22jCG9RQbv8H}`+}9^zSk`nlR8?Z&G2dlDy$4#+WOlg;VHqzuE=fM@ z?OI6HEJH4&tA?FVG}9>jAnq_^tlw8NbjNhfqk2rQr?h(F&WiKy03Sn=-;ZJRh~JrD zbt)zLbnabttEZ>zUiu`N*u4sfQaLE8-WDn@tHp50uD(^r-}UsUUu)`!Rl1PozAc!a z?uj|2QDQ%oV-jxUJmJycySBINSKdX{kDYRS=+`HgR2GO19fg&lZKyBFbbXhQV~v~L za^U944F1_GtuFXtvDdDNDvp<`fqy);>Vw=ncy!NB85Tw{&sT5&Ox%-p%8fTS;OzlRBwErvO+ROe?{%q-Zge=%Up|D4L#>4K@Ke=x%?*^_^P*KD zgXueMiS63!sEw@fNLB-i^F|@Oib+S4bcy{eu&e}Xvb^(mA!=U=Xr3||IpV~3K zQWzEsUeX_qBe6fky#M zzOJm5b+l;~>=sdp%i}}0h zO?B?i*W;Ndn02Y0GUUPxERG`3Bjtj!NroLoYtyVdLtl?SE*CYpf4|_${ku2s`*_)k zN=a}V8_2R5QANlxsq!1BkT6$4>9=-Ix4As@FSS;1q^#TXPrBsw>hJ}$jZ{kUHoP+H zvoYiR39gX}2OHIBYCa~6ERRPJ#V}RIIZakUmuIoLF*{sO8rAUEB9|+A#C|@kw5>u0 zBd=F!4I)Be8ycH*)X1-VPiZ+Ts8_GB;YW&ZFFUo|Sw|x~ZajLsp+_3gv((Q#N>?Jz zFBf`~p_#^${zhPIIJY~yo!7$-xi2LK%3&RkFg}Ax)3+dFCjGgKv^1;lUzQlPo^E{K zmCnrwJ)NuSaJEmueEPO@(_6h3f5mFffhkU9r8A8(JC5eOkux{gPmx_$Uv&|hyj)gN zd>JP8l2U&81@1Hc>#*su2xd{)T`Yw< zN$dSLUN}dfx)Fu`NcY}TuZ)SdviT{JHaiYgP4~@`x{&h*Hd>c3K_To9BnQi@;tuoL z%PYQo&{|IsM)_>BrF1oB~+`2_uZQ48z9!)mtUR zdfKE+b*w8cPu;F6RYJiYyV;PRBbThqHBEu_(U{(gGtjM}Zi$pL8Whx}<JwE3RM0F8x7%!!s)UJVq|TVd#hf1zVLya$;mYp(^oZQ2>=ZXU1c$}f zm|7kfk>=4KoQoQ!2&SOW5|JP1)%#55C$M(u4%SP~tHa&M+=;YsW=v(Old9L3(j)`u z2?#fK&1vtS?G6aOt@E`gZ9*qCmyvc>Ma@Q8^I4y~f3gs7*d=ATlP>1S zyF=k&6p2;7dn^8?+!wZO5r~B+;@KXFEn^&C=6ma1J7Au6y29iMIxd7#iW%=iUzq&C=$aPLa^Q zncia$@TIy6UT@69=nbty5epP>*fVW@5qbUcb2~Gg75dNd{COFLdiz3}kODn^U*=@E z0*$7u7Rl2u)=%fk4m8EK1ctR!6%Ve`e!O20L$0LkM#f+)n9h^dn{n`T*^~d+l*Qlx z$;JC0P9+en2Wlxjwq#z^a6pdnD6fJM!GV7_%8%c)kc5LZs_G^qvw)&J#6WSp< zmsd~1-(GrgjC56Pdf6#!dt^y8Rg}!#UXf)W%~PeU+kU`FeSZHk)%sFv++#Dujk-~m zFHvVJC}UBn2jN& zs!@nZ?e(iyZPNo`p1i#~wsv9l@#Z|ag3JR>0#u1iW9M1RK1iF6-RbJ4KYg?B`dET9 zyR~DjZ>%_vWYm*Z9_+^~hJ_|SNTzBKx=U0l9 z9x(J96b{`R)UVQ$I`wTJ@$_}`)_DyUNOso6=WOmQKI1e`oyYy1C&%AQU<0-`(ow)1 zT}gYdwWdm4wW6|K)LcfMe&psE0XGhMy&xS`@vLi|1#Za{D6l@#D!?nW87wcscUZgELT{Cz**^;Zb~7 z(~WFRO`~!WvyZAW-8v!6n&j*PLm9NlN}BuUN}@E^TX*4Or#dMMF?V9KBeLSiLO4?B zcE3WNIa-H{ThrlCoN=XjOGk1dT=xwwrmt<1a)mrRzg{35`@C!T?&_;Q4Ce=5=>z^*zE_c(0*vWo2_#TD<2)pLXV$FlwP}Ik74IdDQU@yhkCr5h zn5aa>B7PWy5NQ!vf7@p_qtC*{dZ8zLS;JetPkHi>IvPjtJ#ThGQD|Lq#@vE2xdl%`x4A8xOln}BiQ92Po zW;0%A?I5CQ_O`@Ad=`2BLPPbBuPUp@Hb%a_OOI}y{Rwa<#h z5^6M}s7VzE)2&I*33pA>e71d78QpF>sNK;?lj^Kl#wU7G++`N_oL4QPd-iPqBhhs| z(uVM}$ItF-onXuuXO}o$t)emBO3Hjfyil@*+GF;9j?`&67GBM;TGkLHi>@)rkS4Nj zAEk;u)`jc4C$qN6WV2dVd#q}2X6nKt&X*}I@jP%Srs%%DS92lpDY^K*Sx4`l;aql$ zt*-V{U&$DM>pdO?%jt$t=vg5|p+Rw?SPaLW zB6nvZ69$ne4Z(s$3=Rf&RX8L9PWMV*S0@R zuIk&ba#s6sxVZ51^4Kon46X^9`?DC9mEhWB3f+o4#2EXFqy0(UTc>GU| zGCJmI|Dn-dX#7|_6(fT)>&YQ0H&&JX3cTvAq(a@ydM4>5Njnuere{J8p;3?1az60* z$1E7Yyxt^ytULeokgDnRVKQw9vzHg1>X@@jM$n$HBlveIrKP5-GJq%iWH#odVwV6cF^kKX(@#%%uQVb>#T6L^mC@)%SMd4DF? zVky!~ge27>cpUP1Vi}Z32lbLV+CQy+T5Wdmva6Fg^lKb!zrg|HPU=5Qu}k;4GVH+x z%;&pN1LOce0w@9i1Mo-Y|7|z}fbch@BPp2{&R-5{GLoeu8@limQmFF zaJRR|^;kW_nw~0V^ zfTnR!Ni*;-%oSHG1yItARs~uxra|O?YJxBzLjpeE-=~TO3Dn`JL5Gz;F~O1u3|FE- zvK2Vve`ylc`a}G`gpHg58Cqc9fMoy1L}7x7T>%~b&irrNMo?np3`q;d3d;zTK>nrK zOjPS{@&74-fA7j)8uT9~*g23uGnxwIVj9HorzUX#s0pcp2?GH6i}~+kv9fWChtPa_ z@T3m+$0pbjdQw7jcnHn;Pi85hk_u2-1^}c)LNvjdam8K-XJ+KgKQ%!?2n_!#{$H|| zLO=%;hRo6EDmnOBKCL9Cg~ETU##@u^W_5joZ%Et%X_n##%JDOcsO=0VL|Lkk!VdRJ z^|~2pB@PUspT?NOeO?=0Vb+fAGc!j%Ufn-cB`s2A~W{Zj{`wqWq_-w0wr@6VrM zbzni@8c>WS!7c&|ZR$cQ;`niRw{4kG#e z70e!uX8VmP23SuJ*)#(&R=;SxGAvq|&>geL&!5Z7@0Z(No*W561n#u$Uc`f9pD70# z=sKOSK|bF~#khTTn)B28h^a1{;>EaRnHj~>i=Fnr3+Fa4 z`^+O5_itS#7kPd20rq66_wH`%?HNzWk@XFK0n;Z@Cx{kx==2L22zWH$Yg?7 zvDj|u{{+NR3JvUH({;b*$b(U5U z7(lF!1bz2%06+|-v(D?2KgwNw7( zJB#Tz+ZRi&U$i?f34m7>uTzO#+E5cbaiQ&L}UxyOQq~afbNB4EI{E04ZWg53w0A{O%qo=lF8d zf~ktGvIgf-a~zQoWf>loF7pOodrd0a2|BzwwPDV}ShauTK8*fmF6NRbO>Iw9zZU}u zw8Ya}?seBnEGQDmH#XpUUkj}N49tP<2jYwTFp!P+&Fd(%Z#yo80|5@zN(D{_pNow*&4%ql zW~&yp@scb-+Qj-EmErY+Tu=dUmf@*BoXY2&oKT8U?8?s1d}4a`Aq>7SV800m$FE~? zjmz(LY+Xx9sDX$;vU`xgw*jLw7dWOnWWCO8o|;}f>cu0Q&`0I{YudMn;P;L3R-uz# zfns_mZED_IakFBPP2r_S8XM$X)@O-xVKi4`7373Jkd5{2$M#%cRhWer3M(vr{S6>h zj{givZJ3(`yFL@``(afn&~iNx@B1|-qfYiZu?-_&Z8+R~v`d6R-}EX9IVXWO-!hL5 z*k6T#^2zAXdardU3Ao~I)4DGdAv2bx{4nOK`20rJo>rmk3S2ZDu}))8Z1m}CKigf0 z3L`3Y`{huj`xj9@`$xTZzZc3je?n^yG<8sw$`Y%}9mUsjUR%T!?k^(q)6FH6Af^b6 zlPg~IEwg0y;`t9y;#D+uz!oE4VP&Je!<#q*F?m5L5?J3i@!0J6q#eu z!RRU`-)HeqGi_UJZ(n~|PSNsv+Wgl{P-TvaUQ9j?ZCtvb^37U$sFpBrkT{7Jpd?HpIvj2!}RIq zH{9~+gErN2+}J`>Jvng2hwM`=PLNkc7pkjblKW|+Fk9rc)G1R>Ww>RC=r-|!m-u7( zc(a$9NG}w#PjWNMS~)o=i~WA&4L(YIW25@AL9+H9!?3Y}sv#MOdY{bb9j>p`{?O(P zIvb`n?_(gP2w3P#&91JX*md+bBEr%xUHMVqfB;(f?OPtMnAZ#rm5q5mh;a2f_si2_ z3oXWB?{NF(JtkAn6F(O{z@b76OIqMC$&oJ_&S|YbFJ*)3qVX_uNf5b8(!vGX19hsG z(OP>RmZp29KH9Ge2kKjKigUmOe^K_!UXP`von)PR8Qz$%=EmOB9xS(ZxE_tnyzo}7 z=6~$~9k0M~v}`w={AeqF?_)9q{m8K#6M{a&(;u;O41j)I$^T?lx5(zlebpY@NT&#N zR+1bB)-1-xj}R8uwqwf=iP1GbxBjneCC%UrSdSxK1vM^i9;bUkS#iRZw2H>rS<2<$ zNT3|sDH>{tXb=zq7XZi*K?#Zsa1h1{h5!Tq_YbKFm_*=A5-<~j63he;4`77!|LBlo zR^~tR3yxcU=gDFbshyF6>o0bdp$qmHS7D}m3;^QZq9kBBU|9$N-~oU?G5;jyFR7>z hN`IR97YZXIo@y!QgFWddJ3|0`sjFx!m))><{BI=FK%f8s literal 0 HcmV?d00001 diff --git a/packages/file_picker/file_picker/example/web/index.html b/packages/file_picker/file_picker/example/web/index.html new file mode 100644 index 000000000000..9b7a438f823a --- /dev/null +++ b/packages/file_picker/file_picker/example/web/index.html @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + example + + + + + + + + diff --git a/packages/file_picker/file_picker/example/web/manifest.json b/packages/file_picker/file_picker/example/web/manifest.json new file mode 100644 index 000000000000..8c012917dab7 --- /dev/null +++ b/packages/file_picker/file_picker/example/web/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "example", + "short_name": "example", + "start_url": ".", + "display": "standalone", + "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" + } + ] +} diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart new file mode 100644 index 000000000000..2b712eb127c7 --- /dev/null +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -0,0 +1,14 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; + +/// Gets message from platform implementation +Future getMessage() async { + final String result = await FilePickerPlatform.instance.getMessage(); + return result; +} + diff --git a/packages/file_picker/file_picker/pubspec.yaml b/packages/file_picker/file_picker/pubspec.yaml new file mode 100644 index 000000000000..4679320c134a --- /dev/null +++ b/packages/file_picker/file_picker/pubspec.yaml @@ -0,0 +1,32 @@ +name: file_picker +description: Flutter plugin for launching a URL on Android and iOS. Supports + web, phone, SMS, and email schemes. +homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher +version: 0.1.0 + +flutter: + plugin: + platforms: + web: + default_package: file_picker_web + +dependencies: + flutter: + sdk: flutter + file_picker_platform_interface: + path: ../file_picker_platform_interface + file_picker_web: + path: ../file_picker_web + + +dev_dependencies: + flutter_test: + sdk: flutter + test: ^1.3.0 + mockito: ^4.1.1 + plugin_platform_interface: ^1.0.0 + pedantic: ^1.8.0 + +environment: + sdk: ">=2.1.0 <3.0.0" + flutter: ">=1.12.13+hotfix.5 <2.0.0" diff --git a/packages/file_picker/file_picker/test/file_picker_test.dart b/packages/file_picker/file_picker/test/file_picker_test.dart new file mode 100644 index 000000000000..6940fcfb4a6e --- /dev/null +++ b/packages/file_picker/file_picker/test/file_picker_test.dart @@ -0,0 +1,8 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +void main() { +} + diff --git a/packages/file_picker/file_picker_platform_interface/CHANGELOG.md b/packages/file_picker/file_picker_platform_interface/CHANGELOG.md new file mode 100644 index 000000000000..6073234226b2 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.1.0 + +* Initial release. diff --git a/packages/file_picker/file_picker_platform_interface/LICENSE b/packages/file_picker/file_picker_platform_interface/LICENSE new file mode 100644 index 000000000000..c89293372cf3 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/file_picker/file_picker_platform_interface/README.md b/packages/file_picker/file_picker_platform_interface/README.md new file mode 100644 index 000000000000..498936296468 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/README.md @@ -0,0 +1,26 @@ +# file_picker_platform_interface + +A common platform interface for the `file_picker` plugin. + +This interface allows platform-specific implementations of the `file_picker` +plugin, as well as the plugin itself, to ensure they are supporting the +same interface. + +# Usage + +To implement a new platform-specific implementation of `file_picker`, extend +[`FilePickerPlatform`][2] with an implementation that performs the +platform-specific behavior, and when you register your plugin, set the default +`FilePickerPlatform` by calling +`FilePickerPlatform.instance = MyPlatformFilePicker()`. + +# Note on breaking changes + +Strongly prefer non-breaking changes (such as adding a method to the interface) +over breaking changes for this package. + +See https://flutter.dev/go/platform-interface-breaking-changes for a discussion +on why a less-clean interface is preferable to a breaking change. + +[1]: ../file_picker +[2]: lib/file_picker_platform_interface.dart diff --git a/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart new file mode 100644 index 000000000000..d0f557cff059 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart @@ -0,0 +1,43 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'method_channel_file_picker.dart'; + +/// 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 +/// [FilePickerPlatform] methods. +abstract class FilePickerPlatform extends PlatformInterface { + /// Constructs a FilePickerPlatform. + FilePickerPlatform() : super(token: _token); + + static final Object _token = Object(); + + static FilePickerPlatform _instance = MethodChannelFilePicker(); + + /// The default instance of [FilePickerPlatform] to use. + /// + /// Defaults to [MethodChannelFilePicker]. + static FilePickerPlatform get instance => _instance; + + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [FilePickerPlatform] when they register themselves. + static set instance(FilePickerPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + + /// Returns the message from each platform implementation + Future getMessage() { + throw UnimplementedError('getMessage() has not been implemented.'); + } +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/lib/method_channel_file_picker.dart new file mode 100644 index 000000000000..debaaab8b15e --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/method_channel_file_picker.dart @@ -0,0 +1,21 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:meta/meta.dart' show required; + +import 'file_picker_platform_interface.dart'; + +const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); + +/// An implementation of [FilePickerPlatform] that uses method channels. +class MethodChannelFilePicker extends FilePickerPlatform { + + @override + Future getMessage() { + return _channel.invokeMethod('getMessage'); + } +} diff --git a/packages/file_picker/file_picker_platform_interface/pubspec.yaml b/packages/file_picker/file_picker_platform_interface/pubspec.yaml new file mode 100644 index 000000000000..359de184bfc2 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/pubspec.yaml @@ -0,0 +1,22 @@ +name: file_picker_platform_interface +description: A common platform interface for the file_picker plugin. +homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_platform_interface +# NOTE: We strongly prefer non-breaking changes, even at the expense of a +# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes +version: 0.1.0 + +dependencies: + flutter: + sdk: flutter + meta: ^1.0.5 + plugin_platform_interface: ^1.0.1 + +dev_dependencies: + flutter_test: + sdk: flutter + mockito: ^4.1.1 + pedantic: ^1.8.0 + +environment: + sdk: ">=2.1.0 <3.0.0" + flutter: ">=1.9.1+hotfix.4 <2.0.0" diff --git a/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart new file mode 100644 index 000000000000..cb0f11a529f9 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart @@ -0,0 +1,14 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:mockito/mockito.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'package:file_picker_platform_interface/method_channel_file_picker.dart'; +import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; + +void main() { +} \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/CHANGELOG.md b/packages/file_picker/file_picker_web/CHANGELOG.md new file mode 100644 index 000000000000..7be26166a1f2 --- /dev/null +++ b/packages/file_picker/file_picker_web/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.1.0 + +- Initial open-source release. diff --git a/packages/file_picker/file_picker_web/LICENSE b/packages/file_picker/file_picker_web/LICENSE new file mode 100644 index 000000000000..0c382ce171cc --- /dev/null +++ b/packages/file_picker/file_picker_web/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/file_picker/file_picker_web/README.md b/packages/file_picker/file_picker_web/README.md new file mode 100644 index 000000000000..e35424c8d320 --- /dev/null +++ b/packages/file_picker/file_picker_web/README.md @@ -0,0 +1,37 @@ +# file_picker_web + +The web implementation of [`file_picker`][1]. + +**Please set your constraint to `file_picker_web: '>=0.1.y+x <2.0.0'`** + +## Backward compatible 1.0.0 version is coming +The plugin has reached a stable API, we guarantee that version `1.0.0` will be backward compatible with `0.1.y+z`. +Please use `file_picker_web: '>=0.1.y+x <2.0.0'` as your dependency constraint to allow a smoother ecosystem migration. +For more details see: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 + +## Usage + +### Import the package +To use this plugin in your Flutter Web app, simply add it as a dependency in +your pubspec alongside the base `file_picker` plugin. + +_(This is only temporary: in the future we hope to make this package an +"endorsed" implementation of `file_picker`, so that it is automatically +included in your Flutter Web app when you depend on `package:file_picker`.)_ + +This is what the above means to your `pubspec.yaml`: + +```yaml +... +dependencies: + ... + file_picker: ^0.1.0 + file_picker_web: ^0.1.0 + ... +``` + +### Use the plugin +Once you have the `file_picker_web` dependency in your pubspec, you should +be able to use `package:file_picker` as normal. + +[1]: ../file_picker/file_picker diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart new file mode 100644 index 000000000000..36479a275b1c --- /dev/null +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -0,0 +1,20 @@ +import 'dart:async'; + +import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; + +/// The web implementation of [FilePickerPlatform]. +/// +/// This class implements the `package:file_picker` functionality for the web. +class FilePickerPlugin extends FilePickerPlatform { + /// Registers this class as the default instance of [FilePickerPlatform]. + static void registerWith(Registrar registrar) { + FilePickerPlatform.instance = FilePickerPlugin(); + } + + + @override + Future getMessage() { + return Future.value("Hello from the web implementation of file_picker!"); + } +} diff --git a/packages/file_picker/file_picker_web/pubspec.yaml b/packages/file_picker/file_picker_web/pubspec.yaml new file mode 100644 index 000000000000..57cde59325a3 --- /dev/null +++ b/packages/file_picker/file_picker_web/pubspec.yaml @@ -0,0 +1,35 @@ +name: file_picker_web +description: Web platform implementation of file_picker +homepage: https://github.com/flutter/plugins/tree/master/packages/file_picker/file_picker +# 0.1.y+z is compatible with 1.0.0, if you land a breaking change bump +# the version to 2.0.0. +# See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 +version: 0.1.0 + +flutter: + plugin: + platforms: + web: + pluginClass: FilePickerPlugin + fileName: file_picker_web.dart + +dependencies: + file_picker_platform_interface: + path: ../file_picker_platform_interface + platform_detect: ^1.4.0 + flutter: + sdk: flutter + flutter_web_plugins: + sdk: flutter + meta: ^1.1.7 + +dev_dependencies: + flutter_test: + sdk: flutter + url_launcher: ^5.2.5 + pedantic: ^1.8.0 + mockito: ^4.1.1 + +environment: + sdk: ">=2.2.0 <3.0.0" + flutter: ">=1.10.0 <2.0.0" diff --git a/packages/file_picker/file_picker_web/test/file_picker_web_test.dart b/packages/file_picker/file_picker_web/test/file_picker_web_test.dart new file mode 100644 index 000000000000..d471ab5e3446 --- /dev/null +++ b/packages/file_picker/file_picker_web/test/file_picker_web_test.dart @@ -0,0 +1,17 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@TestOn('chrome') // Uses web-only Flutter SDK + +import 'dart:html' as html; +import 'package:flutter_test/flutter_test.dart'; +import 'package:file_picker_web/file_picker_web.dart'; +import 'package:mockito/mockito.dart'; + +import 'package:platform_detect/test_utils.dart' as platform; + +class MockWindow extends Mock implements html.Window {} + +void main() { +} \ No newline at end of file From 390c1fee70aa7753676053daa20c339e172ad6c2 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Thu, 16 Jul 2020 17:58:08 -0700 Subject: [PATCH 002/151] Add DOM Initialization and Test a[download] --- .../file_picker/example/lib/main.dart | 2 +- .../file_picker_web/lib/file_picker_web.dart | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index 00edac95ff8f..5616a5ebc988 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -60,7 +60,7 @@ class _MyHomePageState extends State { ), SizedBox(height: 10), RaisedButton( - child: Text('Press for a message'), + child: Text('Press for a message and a file'), onPressed: () => { _getMessage() }, ), ], diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 36479a275b1c..d69d55ef4fa3 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -1,20 +1,56 @@ import 'dart:async'; +import 'dart:html' as html; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +final String _kFilePickerInputsDomId = '__file_picker_web-file-input'; + /// The web implementation of [FilePickerPlatform]. /// /// This class implements the `package:file_picker` functionality for the web. class FilePickerPlugin extends FilePickerPlatform { + html.Element _target; + + /// Default constructor, initializes _target to a DOM element + /// that we can use to host HTML elements + FilePickerPlugin() { + _target = _ensureInitialized(_kFilePickerInputsDomId); + } + /// Registers this class as the default instance of [FilePickerPlatform]. static void registerWith(Registrar registrar) { FilePickerPlatform.instance = FilePickerPlugin(); } + /// Test download attribute. + void _downloadTest() { + html.AnchorElement element = html.AnchorElement( + href: 'data:text/plain,Hello%2C%20World!', + ); + element.download = 'TestFile'; + + _target.children.clear(); + _target.children.add(element); + element.click(); + } @override Future getMessage() { + _downloadTest(); return Future.value("Hello from the web implementation of file_picker!"); } + + /// Initializes a DOM container where we can host input elements. + html.Element _ensureInitialized(String id) { + var target = html.querySelector('#${id}'); + if (target == null) { + final html.Element targetElement = + html.Element.tag('flt-image-picker-inputs')..id = id; + + html.querySelector('body').children.add(targetElement); + target = targetElement; + } + return target; + } } From 234c81190cb6d5cafa69c740675569922cfbf9e0 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Sun, 19 Jul 2020 15:52:40 -0700 Subject: [PATCH 003/151] Add FilePicker Type + Add TextField to Example --- .../file_picker/example/lib/main.dart | 17 +++++ .../lib/file_picker_platform_interface.dart | 45 +----------- .../method_channel_file_picker.dart | 2 +- .../file_picker_interface.dart | 43 ++++++++++++ .../lib/src/types/camera_device.dart | 18 +++++ .../lib/src/types/image_source.dart | 12 ++++ .../lib/src/types/lost_data_response.dart | 52 ++++++++++++++ .../lib/src/types/picked_file/base.dart | 70 +++++++++++++++++++ .../lib/src/types/picked_file/html.dart | 61 ++++++++++++++++ .../lib/src/types/picked_file/io.dart | 47 +++++++++++++ .../lib/src/types/picked_file/lost_data.dart | 49 +++++++++++++ .../src/types/picked_file/picked_file.dart | 4 ++ .../src/types/picked_file/unsupported.dart | 26 +++++++ .../lib/src/types/retrieve_type.dart | 12 ++++ .../lib/src/types/types.dart | 11 +++ .../pubspec.yaml | 1 + .../test/method_channel_file_picker.dart | 3 - .../file_picker_web/lib/file_picker_web.dart | 2 +- 18 files changed, 427 insertions(+), 48 deletions(-) rename packages/file_picker/file_picker_platform_interface/lib/{ => src/method_channel}/method_channel_file_picker.dart (91%) create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/camera_device.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/image_source.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/lost_data_response.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/base.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/html.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/io.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/lost_data.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/picked_file.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/unsupported.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/retrieve_type.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index 5616a5ebc988..b2f441f3e301 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -36,6 +36,13 @@ class MyHomePage extends StatefulWidget { /// State of Home Page class _MyHomePageState extends State { String _msg = ""; + final TextEditingController _controller = TextEditingController(); + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } void _getMessage() async { String msg = await getMessage(); @@ -55,6 +62,16 @@ class _MyHomePageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ + Container( + width: 150, + child: TextField( + textAlign: TextAlign.center, + controller: _controller, + decoration: InputDecoration( + hintText: 'Enter File Contents', + ), + ), + ), Text( this._msg ), diff --git a/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart index d0f557cff059..f851916ca9e4 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart @@ -1,43 +1,2 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -import 'method_channel_file_picker.dart'; - -/// 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 -/// [FilePickerPlatform] methods. -abstract class FilePickerPlatform extends PlatformInterface { - /// Constructs a FilePickerPlatform. - FilePickerPlatform() : super(token: _token); - - static final Object _token = Object(); - - static FilePickerPlatform _instance = MethodChannelFilePicker(); - - /// The default instance of [FilePickerPlatform] to use. - /// - /// Defaults to [MethodChannelFilePicker]. - static FilePickerPlatform get instance => _instance; - - /// Platform-specific plugins should set this with their own platform-specific - /// class that extends [FilePickerPlatform] when they register themselves. - static set instance(FilePickerPlatform instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - - /// Returns the message from each platform implementation - Future getMessage() { - throw UnimplementedError('getMessage() has not been implemented.'); - } -} +export 'src/platform_interface/file_picker_interface.dart'; +export 'src/types/types.dart'; diff --git a/packages/file_picker/file_picker_platform_interface/lib/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart similarity index 91% rename from packages/file_picker/file_picker_platform_interface/lib/method_channel_file_picker.dart rename to packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart index debaaab8b15e..eac349069202 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/method_channel_file_picker.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart @@ -7,7 +7,7 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:meta/meta.dart' show required; -import 'file_picker_platform_interface.dart'; +import '../platform_interface/file_picker_interface.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart new file mode 100644 index 000000000000..22a3508b4f5a --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -0,0 +1,43 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import '../method_channel/method_channel_file_picker.dart'; + +/// 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 +/// [FilePickerPlatform] methods. +abstract class FilePickerPlatform extends PlatformInterface { + /// Constructs a FilePickerPlatform. + FilePickerPlatform() : super(token: _token); + + static final Object _token = Object(); + + static FilePickerPlatform _instance = MethodChannelFilePicker(); + + /// The default instance of [FilePickerPlatform] to use. + /// + /// Defaults to [MethodChannelFilePicker]. + static FilePickerPlatform get instance => _instance; + + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [FilePickerPlatform] when they register themselves. + static set instance(FilePickerPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + + /// Returns the message from each platform implementation + Future getMessage() { + throw UnimplementedError('getMessage() has not been implemented.'); + } +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/camera_device.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/camera_device.dart new file mode 100644 index 000000000000..6c70fd451a0e --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/camera_device.dart @@ -0,0 +1,18 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Which camera to use when picking images/videos while source is `ImageSource.camera`. +/// +/// Not every device supports both of the positions. +enum CameraDevice { + /// Use the rear camera. + /// + /// In most of the cases, it is the default configuration. + rear, + + /// Use the front camera. + /// + /// Supported on all iPhones/iPads and some Android devices. + front, +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/image_source.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/image_source.dart new file mode 100644 index 000000000000..37981e3038f1 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/image_source.dart @@ -0,0 +1,12 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Specifies the source where the picked image should come from. +enum ImageSource { + /// Opens up the device camera, letting the user to take a new picture. + camera, + + /// Opens the user's photo gallery. + gallery, +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/lost_data_response.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/lost_data_response.dart new file mode 100644 index 000000000000..73014654f6e6 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/lost_data_response.dart @@ -0,0 +1,52 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:flutter/services.dart'; +import 'types.dart'; // TODO: use 'package:...' instead + +/// The response object of [ImagePicker.retrieveLostData]. +/// +/// Only applies to Android. +/// See also: +/// * [ImagePicker.retrieveLostData] for more details on retrieving lost data. +@Deprecated('Use methods that return a LostData object instead.') +class LostDataResponse { + /// Creates an instance with the given [file], [exception], and [type]. Any of + /// the params may be null, but this is never considered to be empty. + LostDataResponse({this.file, this.exception, this.type}); + + /// Initializes an instance with all member params set to null and considered + /// to be empty. + LostDataResponse.empty() + : file = null, + exception = null, + type = null, + _empty = true; + + /// Whether it is an empty response. + /// + /// An empty response should have [file], [exception] and [type] to be null. + bool get isEmpty => _empty; + + /// The file that was lost in a previous [pickImage] or [pickVideo] call due to MainActivity being destroyed. + /// + /// Can be null if [exception] exists. + final File file; + + /// The exception of the last [pickImage] or [pickVideo]. + /// + /// If the last [pickImage] or [pickVideo] threw some exception before the MainActivity destruction, this variable keeps that + /// exception. + /// You should handle this exception as if the [pickImage] or [pickVideo] got an exception when the MainActivity was not destroyed. + /// + /// Note that it is not the exception that caused the destruction of the MainActivity. + final PlatformException exception; + + /// Can either be [RetrieveType.image] or [RetrieveType.video]; + final RetrieveType type; + + bool _empty = false; +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/base.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/base.dart new file mode 100644 index 000000000000..052f870e2e04 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/base.dart @@ -0,0 +1,70 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:meta/meta.dart'; + +/// The interface for a PickedFile. +/// +/// A PickedFile is a container that wraps the path of a selected +/// file by the user and (in some platforms, like web) the bytes +/// with the contents of the file. +/// +/// This class is a very limited subset of dart:io [File], so all +/// the methods should seem familiar. +@immutable +abstract class PickedFileBase { + /// Construct a PickedFile + PickedFileBase(String path); + + /// Get the path of the picked file. + /// + /// This should only be used as a backwards-compatibility clutch + /// for mobile apps, or cosmetic reasons only (to show the user + /// the path they've picked). + /// + /// Accessing the data contained in the picked file by its path + /// is platform-dependant (and won't work on web), so use the + /// byte getters in the PickedFile instance instead. + String get path { + throw UnimplementedError('.path has not been implemented.'); + } + + /// The name of the file as it was selected by the user in their device. + /// + /// Use only for cosmetic reasons, do not try to use this as a path. + String get name { + throw UnimplementedError('.name has not been implemented.'); + } + + /// Get the length of the file. Returns a `Future` that completes with the length in bytes. + Future length() { + throw UnimplementedError('.length() has not been implemented.'); + } + + /// Synchronously read the entire file contents as a string using the given [Encoding]. + /// + /// By default, `encoding` is [utf8]. + /// + /// Throws Exception if the operation fails. + Future readAsString({Encoding encoding = utf8}) { + throw UnimplementedError('readAsString() has not been implemented.'); + } + + /// Synchronously read the entire file contents as a list of bytes. + /// + /// Throws Exception if the operation fails. + Future readAsBytes() { + throw UnimplementedError('readAsBytes() has not been implemented.'); + } + + /// Create a new independent [Stream] for the contents of this file. + /// + /// If `start` is present, the file will be read from byte-offset `start`. Otherwise from the beginning (index 0). + /// + /// If `end` is present, only up to byte-index `end` will be read. Otherwise, until end of file. + /// + /// In order to make sure that system resources are freed, the stream must be read to completion or the subscription on the stream must be cancelled. + Stream openRead([int start, int end]) { + throw UnimplementedError('openRead() has not been implemented.'); + } +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/html.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/html.dart new file mode 100644 index 000000000000..9be3c23903dd --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/html.dart @@ -0,0 +1,61 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:http/http.dart' as http show readBytes; + +import './base.dart'; + +/// A PickedFile that works on web. +/// +/// It wraps the bytes of a selected file. +class PickedFile extends PickedFileBase { + final String path; + final Uint8List _initBytes; + final int _length; + @override + final String name; + + /// Construct a PickedFile object from its ObjectUrl. + /// + /// Optionally, this can be initialized with `bytes` and `length` + /// so no http requests are performed to retrieve files later. + /// + /// `name` needs to be passed from the outside, since we only have + /// access to it while we create the ObjectUrl. + PickedFile( + this.path, { + this.name, + int length, + Uint8List bytes, + }) : _initBytes = bytes, + _length = length, + super(path); + + Future get _bytes async { + if (_initBytes != null) { + return Future.value(UnmodifiableUint8ListView(_initBytes)); + } + return http.readBytes(path); + } + + @override + Future length() async { + return _length ?? (await _bytes).length; + } + + @override + Future readAsString({Encoding encoding = utf8}) async { + return encoding.decode(await _bytes); + } + + @override + Future readAsBytes() async { + return Future.value(await _bytes); + } + + @override + Stream openRead([int start, int end]) async* { + final bytes = await _bytes; + yield bytes.sublist(start ?? 0, end ?? bytes.length); + } +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/io.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/io.dart new file mode 100644 index 000000000000..fefa87933cc0 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/io.dart @@ -0,0 +1,47 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import './base.dart'; + +/// A PickedFile backed by a dart:io File. +class PickedFile extends PickedFileBase { + final File _file; + + /// Construct a PickedFile object backed by a dart:io File. + PickedFile(String path) + : _file = File(path), + super(path); + + @override + String get path { + return _file.path; + } + + @override + String get name { + return _file.path.split(Platform.pathSeparator).last; + } + + @override + Future length() { + return _file.length(); + } + + @override + Future readAsString({Encoding encoding = utf8}) { + return _file.readAsString(encoding: encoding); + } + + @override + Future readAsBytes() { + return _file.readAsBytes(); + } + + @override + Stream openRead([int start, int end]) { + return _file + .openRead(start ?? 0, end) + .map((chunk) => Uint8List.fromList(chunk)); + } +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/lost_data.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/lost_data.dart new file mode 100644 index 000000000000..18c5eff5de7f --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/lost_data.dart @@ -0,0 +1,49 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import '../types.dart'; // TODO: use 'package:...' instead + +/// The response object of [ImagePicker.retrieveLostData]. +/// +/// Only applies to Android. +/// See also: +/// * [ImagePicker.retrieveLostData] for more details on retrieving lost data. +class LostData { + /// Creates an instance with the given [file], [exception], and [type]. Any of + /// the params may be null, but this is never considered to be empty. + LostData({this.file, this.exception, this.type}); + + /// Initializes an instance with all member params set to null and considered + /// to be empty. + LostData.empty() + : file = null, + exception = null, + type = null, + _empty = true; + + /// Whether it is an empty response. + /// + /// An empty response should have [file], [exception] and [type] to be null. + bool get isEmpty => _empty; + + /// The file that was lost in a previous [pickImage] or [pickVideo] call due to MainActivity being destroyed. + /// + /// Can be null if [exception] exists. + final PickedFile file; + + /// The exception of the last [pickImage] or [pickVideo]. + /// + /// If the last [pickImage] or [pickVideo] threw some exception before the MainActivity destruction, this variable keeps that + /// exception. + /// You should handle this exception as if the [pickImage] or [pickVideo] got an exception when the MainActivity was not destroyed. + /// + /// Note that it is not the exception that caused the destruction of the MainActivity. + final PlatformException exception; + + /// Can either be [RetrieveType.image] or [RetrieveType.video]; + final RetrieveType type; + + bool _empty = false; +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/picked_file.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/picked_file.dart new file mode 100644 index 000000000000..b2a614ccb304 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/picked_file.dart @@ -0,0 +1,4 @@ +export 'lost_data.dart'; +export 'unsupported.dart' + if (dart.library.html) 'html.dart' + if (dart.library.io) 'io.dart'; diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/unsupported.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/unsupported.dart new file mode 100644 index 000000000000..edccd9dd9d03 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/unsupported.dart @@ -0,0 +1,26 @@ +import 'dart:typed_data'; + +import './base.dart'; + +/// A PickedFile is a cross-platform, simplified File abstraction. +/// +/// It wraps the bytes of a selected file, and its (platform-dependant) path. +class PickedFile extends PickedFileBase { + /// Construct a PickedFile object from its path. + /// + /// Optionally, this can be initialized with `bytes` and `length` + /// so no http requests are performed to retrieve data later. + /// + /// `name` may be passed from the outside, for those cases where the effective + /// `path` of the file doesn't match what the user sees when selecting it + /// (like in web) + PickedFile( + String path, { + String name, + int length, + Uint8List bytes, + }) : super(path) { + throw UnimplementedError( + 'PickedFile is not available in your current platform.'); + } +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/retrieve_type.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/retrieve_type.dart new file mode 100644 index 000000000000..cc32be9711c2 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/retrieve_type.dart @@ -0,0 +1,12 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// The type of the retrieved data in a [LostDataResponse]. +enum RetrieveType { + /// A static picture. See [ImagePicker.pickImage]. + image, + + /// A video. See [ImagePicker.pickVideo]. + video +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart new file mode 100644 index 000000000000..9c44fae1aa9d --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart @@ -0,0 +1,11 @@ +export 'camera_device.dart'; +export 'image_source.dart'; +export 'lost_data_response.dart'; +export 'retrieve_type.dart'; +export 'picked_file/picked_file.dart'; + +/// Denotes that an image is being picked. +const String kTypeImage = 'image'; + +/// Denotes that a video is being picked. +const String kTypeVideo = 'video'; diff --git a/packages/file_picker/file_picker_platform_interface/pubspec.yaml b/packages/file_picker/file_picker_platform_interface/pubspec.yaml index 359de184bfc2..2cca9d6d7a04 100644 --- a/packages/file_picker/file_picker_platform_interface/pubspec.yaml +++ b/packages/file_picker/file_picker_platform_interface/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: flutter: sdk: flutter meta: ^1.0.5 + http: ^0.12.0+1 plugin_platform_interface: ^1.0.1 dev_dependencies: diff --git a/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart index cb0f11a529f9..b5670b96635e 100644 --- a/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart +++ b/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart @@ -7,8 +7,5 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:file_picker_platform_interface/method_channel_file_picker.dart'; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; - void main() { } \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index d69d55ef4fa3..a0287c6acf8a 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -28,7 +28,7 @@ class FilePickerPlugin extends FilePickerPlatform { html.AnchorElement element = html.AnchorElement( href: 'data:text/plain,Hello%2C%20World!', ); - element.download = 'TestFile'; + element.download = ''; _target.children.clear(); _target.children.add(element); From 4fd9c14f6745fd39d9189950772ce196a8c59455 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Sun, 19 Jul 2020 16:09:09 -0700 Subject: [PATCH 004/151] Test Blob URL Instead of Data Scheme --- .../file_picker_web/lib/file_picker_web.dart | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index a0287c6acf8a..3ff843fda388 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -25,8 +25,12 @@ class FilePickerPlugin extends FilePickerPlatform { /// Test download attribute. void _downloadTest() { + html.Blob blob = html.Blob(["Hello World from blob!"], 'text/plain'); + + String url = html.Url.createObjectUrl(blob); + html.AnchorElement element = html.AnchorElement( - href: 'data:text/plain,Hello%2C%20World!', + href: url, ); element.download = ''; @@ -35,6 +39,13 @@ class FilePickerPlugin extends FilePickerPlatform { element.click(); } + /// Web implementation of saveFile() + /// TODO: This should take input PickedFile or similar, not string + @override + Future saveFile(String file_contents) { + + } + @override Future getMessage() { _downloadTest(); From 2d06300a3d45ae884865ffddd07a2b5e9285159b Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 22 Jul 2020 13:53:26 -0700 Subject: [PATCH 005/151] Rename PickedFile to XFile; Implement saveFile with Uint8List --- .../file_picker/example/lib/main.dart | 23 +++++++++--------- .../file_picker/lib/file_picker.dart | 8 +++++++ .../file_picker_interface.dart | 7 ++++++ .../lib/src/types/types.dart | 2 +- .../types/{picked_file => x_file}/base.dart | 13 +++++----- .../types/{picked_file => x_file}/html.dart | 9 +++---- .../src/types/{picked_file => x_file}/io.dart | 8 +++---- .../{picked_file => x_file}/lost_data.dart | 4 ++-- .../{picked_file => x_file}/unsupported.dart | 10 ++++---- .../picked_file.dart => x_file/x_file.dart} | 0 .../file_picker_web/lib/file_picker_web.dart | 24 ++++++++----------- 11 files changed, 60 insertions(+), 48 deletions(-) rename packages/file_picker/file_picker_platform_interface/lib/src/types/{picked_file => x_file}/base.dart (89%) rename packages/file_picker/file_picker_platform_interface/lib/src/types/{picked_file => x_file}/html.dart (90%) rename packages/file_picker/file_picker_platform_interface/lib/src/types/{picked_file => x_file}/io.dart (81%) rename packages/file_picker/file_picker_platform_interface/lib/src/types/{picked_file => x_file}/lost_data.dart (95%) rename packages/file_picker/file_picker_platform_interface/lib/src/types/{picked_file => x_file}/unsupported.dart (70%) rename packages/file_picker/file_picker_platform_interface/lib/src/types/{picked_file/picked_file.dart => x_file/x_file.dart} (100%) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index b2f441f3e301..5b0cdb12d624 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -1,6 +1,10 @@ +import 'dart:typed_data'; + import 'package:flutter/material.dart'; import 'package:file_picker/file_picker.dart'; + + void main() { runApp(MyApp()); } @@ -35,7 +39,6 @@ class MyHomePage extends StatefulWidget { /// State of Home Page class _MyHomePageState extends State { - String _msg = ""; final TextEditingController _controller = TextEditingController(); @override @@ -44,11 +47,12 @@ class _MyHomePageState extends State { super.dispose(); } - void _getMessage() async { - String msg = await getMessage(); - setState(() { - this._msg = "Here is your message: " + msg; - }); + void _saveFile() async { + Uint8List data; + data = Uint8List.fromList(_controller.text.codeUnits); + + // await? + saveFile(data); } @override @@ -72,13 +76,10 @@ class _MyHomePageState extends State { ), ), ), - Text( - this._msg - ), SizedBox(height: 10), RaisedButton( - child: Text('Press for a message and a file'), - onPressed: () => { _getMessage() }, + child: Text('Press to save file'), + onPressed: () => { _saveFile() }, ), ], ), diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index 2b712eb127c7..d85eb442f24e 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -3,12 +3,20 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:typed_data'; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; +export 'package:file_picker_platform_interface/file_picker_platform_interface.dart' + show XFile; + /// Gets message from platform implementation Future getMessage() async { final String result = await FilePickerPlatform.instance.getMessage(); return result; } +/// Saves File to user's file system +void saveFile(Uint8List data) async { + return FilePickerPlatform.instance.saveFile(data); +} \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index 22a3508b4f5a..653b6f458fe3 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:typed_data'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; @@ -40,4 +41,10 @@ abstract class FilePickerPlatform extends PlatformInterface { Future getMessage() { throw UnimplementedError('getMessage() has not been implemented.'); } + + /// Saves the file to user's Disk + /// TODO: Parameters should not be string + void saveFile(Uint8List data) async { + throw UnimplementedError('saveFile() has not been implemented.'); + } } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart index 9c44fae1aa9d..9b275ed22e97 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart @@ -2,7 +2,7 @@ export 'camera_device.dart'; export 'image_source.dart'; export 'lost_data_response.dart'; export 'retrieve_type.dart'; -export 'picked_file/picked_file.dart'; +export 'x_file/x_file.dart'; /// Denotes that an image is being picked. const String kTypeImage = 'image'; diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/base.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart similarity index 89% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/base.dart rename to packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart index 052f870e2e04..52b6015a9ada 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/base.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart @@ -3,18 +3,17 @@ import 'dart:typed_data'; import 'package:meta/meta.dart'; -/// The interface for a PickedFile. +/// The interface for a XFile. /// -/// A PickedFile is a container that wraps the path of a selected +/// A XFile is a container that wraps the path of a selected /// file by the user and (in some platforms, like web) the bytes /// with the contents of the file. /// /// This class is a very limited subset of dart:io [File], so all /// the methods should seem familiar. -@immutable -abstract class PickedFileBase { - /// Construct a PickedFile - PickedFileBase(String path); +abstract class XFileBase { + /// Construct a XFile + XFileBase(String path); /// Get the path of the picked file. /// @@ -24,7 +23,7 @@ abstract class PickedFileBase { /// /// Accessing the data contained in the picked file by its path /// is platform-dependant (and won't work on web), so use the - /// byte getters in the PickedFile instance instead. + /// byte getters in the XFile instance instead. String get path { throw UnimplementedError('.path has not been implemented.'); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/html.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart similarity index 90% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/html.dart rename to packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart index 9be3c23903dd..e38cff9faf55 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/html.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart @@ -1,28 +1,29 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'dart:html'; import 'package:http/http.dart' as http show readBytes; import './base.dart'; -/// A PickedFile that works on web. +/// A XFile that works on web. /// /// It wraps the bytes of a selected file. -class PickedFile extends PickedFileBase { +class XFile extends XFileBase { final String path; final Uint8List _initBytes; final int _length; @override final String name; - /// Construct a PickedFile object from its ObjectUrl. + /// Construct a XFile object from its ObjectUrl. /// /// Optionally, this can be initialized with `bytes` and `length` /// so no http requests are performed to retrieve files later. /// /// `name` needs to be passed from the outside, since we only have /// access to it while we create the ObjectUrl. - PickedFile( + XFile( this.path, { this.name, int length, diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/io.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart similarity index 81% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/io.dart rename to packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart index fefa87933cc0..3ea492748bd1 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/io.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart @@ -4,12 +4,12 @@ import 'dart:typed_data'; import './base.dart'; -/// A PickedFile backed by a dart:io File. -class PickedFile extends PickedFileBase { +/// A XFile backed by a dart:io File. +class XFile extends XFileBase { final File _file; - /// Construct a PickedFile object backed by a dart:io File. - PickedFile(String path) + /// Construct a XFile object backed by a dart:io File. + XFile(String path) : _file = File(path), super(path); diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/lost_data.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/lost_data.dart similarity index 95% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/lost_data.dart rename to packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/lost_data.dart index 18c5eff5de7f..15bb236caece 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/lost_data.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/lost_data.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. import 'package:flutter/services.dart'; -import '../types.dart'; // TODO: use 'package:...' instead +import '../types.dart'; /// The response object of [ImagePicker.retrieveLostData]. /// @@ -31,7 +31,7 @@ class LostData { /// The file that was lost in a previous [pickImage] or [pickVideo] call due to MainActivity being destroyed. /// /// Can be null if [exception] exists. - final PickedFile file; + final XFile file; /// The exception of the last [pickImage] or [pickVideo]. /// diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/unsupported.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart similarity index 70% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/unsupported.dart rename to packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart index edccd9dd9d03..cf384f5d939b 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/unsupported.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart @@ -2,11 +2,11 @@ import 'dart:typed_data'; import './base.dart'; -/// A PickedFile is a cross-platform, simplified File abstraction. +/// A XFile is a cross-platform, simplified File abstraction. /// /// It wraps the bytes of a selected file, and its (platform-dependant) path. -class PickedFile extends PickedFileBase { - /// Construct a PickedFile object from its path. +class XFile extends XFileBase { + /// Construct a XFile object from its path. /// /// Optionally, this can be initialized with `bytes` and `length` /// so no http requests are performed to retrieve data later. @@ -14,13 +14,13 @@ class PickedFile extends PickedFileBase { /// `name` may be passed from the outside, for those cases where the effective /// `path` of the file doesn't match what the user sees when selecting it /// (like in web) - PickedFile( + XFile( String path, { String name, int length, Uint8List bytes, }) : super(path) { throw UnimplementedError( - 'PickedFile is not available in your current platform.'); + 'XFile is not available in your current platform.'); } } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/picked_file.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/picked_file.dart rename to packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 3ff843fda388..32b6eec30122 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:html' as html; +import 'dart:typed_data'; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; @@ -23,32 +24,27 @@ class FilePickerPlugin extends FilePickerPlatform { FilePickerPlatform.instance = FilePickerPlugin(); } - /// Test download attribute. - void _downloadTest() { - html.Blob blob = html.Blob(["Hello World from blob!"], 'text/plain'); - + /// Web implementation of saveFile() + /// TODO: This should take input PickedFile or similar, not string + @override + void saveFile(Uint8List data, {String suggestedName = ''}) async { + // Create blob from data + // TODO: Handle different types + html.Blob blob = html.Blob([data], 'text/plain'); String url = html.Url.createObjectUrl(blob); + // Create an tag with the appropriate download attributes and click it html.AnchorElement element = html.AnchorElement( href: url, ); - element.download = ''; - + element.download = suggestedName; _target.children.clear(); _target.children.add(element); element.click(); } - /// Web implementation of saveFile() - /// TODO: This should take input PickedFile or similar, not string - @override - Future saveFile(String file_contents) { - - } - @override Future getMessage() { - _downloadTest(); return Future.value("Hello from the web implementation of file_picker!"); } From 2d924da0139023ace9f2f2676f2e675928676ad1 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 22 Jul 2020 14:59:43 -0700 Subject: [PATCH 006/151] PoC loadFile text only --- .../file_picker/example/lib/main.dart | 12 +++++++ .../file_picker/lib/file_picker.dart | 5 +++ .../file_picker_interface.dart | 7 +++- .../file_picker_web/lib/file_picker_web.dart | 34 ++++++++++++++++++- 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index 5b0cdb12d624..f6ed81a76b18 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -55,6 +55,14 @@ class _MyHomePageState extends State { saveFile(data); } + void _loadFile() async { + XFile file = await loadFile(); + + String text = await file.readAsString(); + + _controller.text = text; + } + @override Widget build(BuildContext context) { @@ -81,6 +89,10 @@ class _MyHomePageState extends State { child: Text('Press to save file'), onPressed: () => { _saveFile() }, ), + RaisedButton( + child: Text('Press to load a file'), + onPressed: () => { _loadFile() }, + ), ], ), ), diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index d85eb442f24e..b118e460e892 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -19,4 +19,9 @@ Future getMessage() async { /// Saves File to user's file system void saveFile(Uint8List data) async { return FilePickerPlatform.instance.saveFile(data); +} + +/// Loads File from user's file system +Future loadFile() { + return FilePickerPlatform.instance.loadFile(); } \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index 653b6f458fe3..b0c6d83cb2c8 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'dart:typed_data'; +import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import '../method_channel/method_channel_file_picker.dart'; @@ -42,8 +43,12 @@ abstract class FilePickerPlatform extends PlatformInterface { throw UnimplementedError('getMessage() has not been implemented.'); } + /// Load file from user's computer and return it as an XFile + Future loadFile() { + throw UnimplementedError('loadFile() has not been implemented.'); + } + /// Saves the file to user's Disk - /// TODO: Parameters should not be string void saveFile(Uint8List data) async { throw UnimplementedError('saveFile() has not been implemented.'); } diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 32b6eec30122..713c6f2ebb8f 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -24,8 +24,39 @@ class FilePickerPlugin extends FilePickerPlatform { FilePickerPlatform.instance = FilePickerPlugin(); } + /// Load file from user's computer and return it as an XFile + /// TODO: multiple files + Future loadFile() { + // Create a file input element + html.FileUploadInputElement element = html.FileUploadInputElement(); + element.accept = 'text/plain'; // TODO: accept different types + element.multiple = true; + + // Add the file input element and click it + _target.children.clear(); + _target.children.add(element); + element.click(); + + Completer _completer = Completer(); + + // Get the returned files + // TODO: Handle errors + element.onChange.first.then((event) { + // TODO: Multiple files + html.File files = element.files.first; + String url = html.Url.createObjectUrl(files); + String name = files.name; + int length = files.size; + + XFile loadedFile = XFile(url, name: name, length: length); + + _completer.complete(loadedFile); + }); + + return _completer.future; + } + /// Web implementation of saveFile() - /// TODO: This should take input PickedFile or similar, not string @override void saveFile(Uint8List data, {String suggestedName = ''}) async { // Create blob from data @@ -43,6 +74,7 @@ class FilePickerPlugin extends FilePickerPlatform { element.click(); } + /// "Hello World" function for testing @override Future getMessage() { return Future.value("Hello from the web implementation of file_picker!"); From fccf520eac3470bde637a9ca6c9cc7d57526f075 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 22 Jul 2020 16:56:59 -0700 Subject: [PATCH 007/151] Add suggestedName to saveFile --- .../file_picker/example/lib/main.dart | 29 ++++++++++++++----- .../file_picker/lib/file_picker.dart | 4 +-- .../file_picker_interface.dart | 2 +- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index f6ed81a76b18..c5d58fe939a6 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -39,20 +39,25 @@ class MyHomePage extends StatefulWidget { /// State of Home Page class _MyHomePageState extends State { - final TextEditingController _controller = TextEditingController(); + final TextEditingController _fileController = TextEditingController(); + final TextEditingController _nameController = TextEditingController(); @override void dispose() { - _controller.dispose(); + _fileController.dispose(); super.dispose(); } void _saveFile() async { Uint8List data; - data = Uint8List.fromList(_controller.text.codeUnits); + data = Uint8List.fromList(_fileController.text.codeUnits); // await? - saveFile(data); + if (_nameController.text == '') { + saveFile(data); + } else { + saveFile(data, suggestedName: _nameController.text); + } } void _loadFile() async { @@ -60,7 +65,7 @@ class _MyHomePageState extends State { String text = await file.readAsString(); - _controller.text = text; + _fileController.text = text; } @override @@ -75,10 +80,20 @@ class _MyHomePageState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Container( - width: 150, + width: 300, child: TextField( textAlign: TextAlign.center, - controller: _controller, + controller: _nameController, + decoration: InputDecoration( + hintText: '(Optional) Suggest File Name', + ), + ), + ), + Container( + width: 300, + child: TextField( + textAlign: TextAlign.center, + controller: _fileController, decoration: InputDecoration( hintText: 'Enter File Contents', ), diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index b118e460e892..4a521333e812 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -17,8 +17,8 @@ Future getMessage() async { } /// Saves File to user's file system -void saveFile(Uint8List data) async { - return FilePickerPlatform.instance.saveFile(data); +void saveFile(Uint8List data, {String suggestedName}) async { + return FilePickerPlatform.instance.saveFile(data, suggestedName: suggestedName); } /// Loads File from user's file system diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index b0c6d83cb2c8..4cadf7609a93 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -49,7 +49,7 @@ abstract class FilePickerPlatform extends PlatformInterface { } /// Saves the file to user's Disk - void saveFile(Uint8List data) async { + void saveFile(Uint8List data, {String suggestedName}) async { throw UnimplementedError('saveFile() has not been implemented.'); } } From 0a40194d1c4a6a67b8e6549949cc179fbea2135e Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 22 Jul 2020 17:02:48 -0700 Subject: [PATCH 008/151] Use file name in loadFile example --- packages/file_picker/file_picker/example/lib/main.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index c5d58fe939a6..5108ce4bac16 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -66,6 +66,10 @@ class _MyHomePageState extends State { String text = await file.readAsString(); _fileController.text = text; + + if (file.name != '') { + _nameController.text = file.name; + } } @override From 30bae1d32b2093174b7cdd2f0b5d3cb62339bac1 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Thu, 23 Jul 2020 17:03:15 -0700 Subject: [PATCH 009/151] Load Multiple Files, Save As a Type --- .../file_picker/example/lib/main.dart | 32 ++++++++---- .../file_picker/lib/file_picker.dart | 14 ++---- .../file_picker_interface.dart | 10 +--- .../file_picker_web/lib/file_picker_web.dart | 50 ++++++++++++------- 4 files changed, 61 insertions(+), 45 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index 5108ce4bac16..c0aa37d24023 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -19,7 +19,7 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: MyHomePage(title: 'File Picker Demo Home Page'), + home: MyHomePage(title: 'File Selector Demo Home Page'), ); } } @@ -41,6 +41,7 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { final TextEditingController _fileController = TextEditingController(); final TextEditingController _nameController = TextEditingController(); + final TextEditingController _extensionController = TextEditingController(); @override void dispose() { @@ -54,21 +55,26 @@ class _MyHomePageState extends State { // await? if (_nameController.text == '') { - saveFile(data); + saveFile(data, type: 'text/plain'); } else { - saveFile(data, suggestedName: _nameController.text); + saveFile(data, type: 'text/plain', suggestedName: _nameController.text); } } void _loadFile() async { - XFile file = await loadFile(); + List file; + if (_extensionController.text.isNotEmpty) { + file = await loadFile(acceptedTypes: _extensionController.text.split(',')); + } else { + file = await loadFile(); + } - String text = await file.readAsString(); + String text = await file.first.readAsString(); _fileController.text = text; - if (file.name != '') { - _nameController.text = file.name; + if (file.first.name.isNotEmpty) { + _nameController.text = file.first.name; } } @@ -86,7 +92,6 @@ class _MyHomePageState extends State { Container( width: 300, child: TextField( - textAlign: TextAlign.center, controller: _nameController, decoration: InputDecoration( hintText: '(Optional) Suggest File Name', @@ -96,7 +101,7 @@ class _MyHomePageState extends State { Container( width: 300, child: TextField( - textAlign: TextAlign.center, + maxLines: null, controller: _fileController, decoration: InputDecoration( hintText: 'Enter File Contents', @@ -108,6 +113,15 @@ class _MyHomePageState extends State { child: Text('Press to save file'), onPressed: () => { _saveFile() }, ), + Container( + width: 300, + child: TextField( + controller: _extensionController, + decoration: InputDecoration( + hintText: '(Optional) Accepted Load Extensions', + ), + ), + ), RaisedButton( child: Text('Press to load a file'), onPressed: () => { _loadFile() }, diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index 4a521333e812..e41862beded3 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -10,18 +10,12 @@ import 'package:file_picker_platform_interface/file_picker_platform_interface.da export 'package:file_picker_platform_interface/file_picker_platform_interface.dart' show XFile; -/// Gets message from platform implementation -Future getMessage() async { - final String result = await FilePickerPlatform.instance.getMessage(); - return result; -} - /// Saves File to user's file system -void saveFile(Uint8List data, {String suggestedName}) async { - return FilePickerPlatform.instance.saveFile(data, suggestedName: suggestedName); +void saveFile(Uint8List data, {String type = '', String suggestedName}) async { + return FilePickerPlatform.instance.saveFile(data, type: type, suggestedName: suggestedName); } /// Loads File from user's file system -Future loadFile() { - return FilePickerPlatform.instance.loadFile(); +Future> loadFile({List acceptedTypes}) { + return FilePickerPlatform.instance.loadFile(acceptedTypes: acceptedTypes); } \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index 4cadf7609a93..e089870b1741 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -37,19 +37,13 @@ abstract class FilePickerPlatform extends PlatformInterface { _instance = instance; } - - /// Returns the message from each platform implementation - Future getMessage() { - throw UnimplementedError('getMessage() has not been implemented.'); - } - /// Load file from user's computer and return it as an XFile - Future loadFile() { + Future> loadFile({List acceptedTypes}) { throw UnimplementedError('loadFile() has not been implemented.'); } /// Saves the file to user's Disk - void saveFile(Uint8List data, {String suggestedName}) async { + void saveFile(Uint8List data, {String type, String suggestedName}) async { throw UnimplementedError('saveFile() has not been implemented.'); } } diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 713c6f2ebb8f..9321d3c5a2e4 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -26,10 +26,20 @@ class FilePickerPlugin extends FilePickerPlatform { /// Load file from user's computer and return it as an XFile /// TODO: multiple files - Future loadFile() { + Future> loadFile({List acceptedTypes}) { + String inputString = ''; + for (String element in acceptedTypes) { + if (inputString.isNotEmpty) { + inputString += ','; + } + inputString += element; + } + // Create a file input element html.FileUploadInputElement element = html.FileUploadInputElement(); - element.accept = 'text/plain'; // TODO: accept different types + if (inputString.isNotEmpty) { + element.accept = inputString; + } element.multiple = true; // Add the file input element and click it @@ -37,20 +47,25 @@ class FilePickerPlugin extends FilePickerPlatform { _target.children.add(element); element.click(); - Completer _completer = Completer(); + Completer> _completer = Completer>(); // Get the returned files // TODO: Handle errors element.onChange.first.then((event) { // TODO: Multiple files - html.File files = element.files.first; - String url = html.Url.createObjectUrl(files); - String name = files.name; - int length = files.size; - - XFile loadedFile = XFile(url, name: name, length: length); + List files = element.files; + List returnFiles = List(); + + for (html.File file in files) { + String url = html.Url.createObjectUrl(file); + String name = file.name; + int length = file.size; + + returnFiles.add(new XFile(url, name: name, length: length)); + } + - _completer.complete(loadedFile); + _completer.complete(returnFiles); }); return _completer.future; @@ -58,10 +73,15 @@ class FilePickerPlugin extends FilePickerPlatform { /// Web implementation of saveFile() @override - void saveFile(Uint8List data, {String suggestedName = ''}) async { + void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { // Create blob from data // TODO: Handle different types - html.Blob blob = html.Blob([data], 'text/plain'); + html.Blob blob; + if(type.isEmpty) { + blob = html.Blob([data]); + } else { + blob = html.Blob([data], type); + } String url = html.Url.createObjectUrl(blob); // Create an tag with the appropriate download attributes and click it @@ -74,12 +94,6 @@ class FilePickerPlugin extends FilePickerPlatform { element.click(); } - /// "Hello World" function for testing - @override - Future getMessage() { - return Future.value("Hello from the web implementation of file_picker!"); - } - /// Initializes a DOM container where we can host input elements. html.Element _ensureInitialized(String id) { var target = html.querySelector('#${id}'); From 06cf4fcd1325f38af914b7ee6d0bbb5641ce85d4 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 27 Jul 2020 14:28:55 -0700 Subject: [PATCH 010/151] Add Method Channel Implementation + Some Refactor --- .../method_channel_file_picker.dart | 25 ++++++++++++++++--- .../lib/src/types/x_file/base.dart | 2 -- .../lib/src/types/x_file/html.dart | 1 - .../file_picker_web/lib/file_picker_web.dart | 2 +- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart index eac349069202..89a33a18d0d9 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart @@ -2,20 +2,37 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; +import 'dart:typed_data'; import 'package:flutter/services.dart'; -import 'package:meta/meta.dart' show required; import '../platform_interface/file_picker_interface.dart'; +import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); /// An implementation of [FilePickerPlatform] that uses method channels. class MethodChannelFilePicker extends FilePickerPlatform { + /// Load file from user's computer and return it as an XFile + @override + Future> loadFile({List acceptedTypes}) { + return _channel.invokeMethod>( + 'loadFile', + { + 'acceptedTypes': acceptedTypes, + }, + ); + } + /// Saves the file to user's Disk @override - Future getMessage() { - return _channel.invokeMethod('getMessage'); + void saveFile(Uint8List data, {String type, String suggestedName}) async { + await _channel.invokeMethod( + 'saveFile', + { + 'type': type, + 'suggestedName': suggestedName, + }, + ); } } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart index 52b6015a9ada..4d12145ce14b 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart @@ -1,8 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:meta/meta.dart'; - /// The interface for a XFile. /// /// A XFile is a container that wraps the path of a selected diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart index e38cff9faf55..e5ecd11fee38 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart @@ -1,6 +1,5 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'dart:html'; import 'package:http/http.dart' as http show readBytes; diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 9321d3c5a2e4..2f058da59c26 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -61,7 +61,7 @@ class FilePickerPlugin extends FilePickerPlatform { String name = file.name; int length = file.size; - returnFiles.add(new XFile(url, name: name, length: length)); + returnFiles.add(XFile(url, name: name, length: length)); } From 3303db7dc392fad7ba0bf11811179d478e20acb6 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 27 Jul 2020 15:01:25 -0700 Subject: [PATCH 011/151] Initial Refactor Change some variables to final, condense for loops and some if statements. --- .../file_picker_web/lib/file_picker_web.dart | 51 ++++++++----------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 2f058da59c26..281e2b49ed15 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'dart:html' as html; +import 'dart:html'; import 'dart:typed_data'; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; @@ -11,7 +11,7 @@ final String _kFilePickerInputsDomId = '__file_picker_web-file-input'; /// /// This class implements the `package:file_picker` functionality for the web. class FilePickerPlugin extends FilePickerPlatform { - html.Element _target; + Element _target; /// Default constructor, initializes _target to a DOM element /// that we can use to host HTML elements @@ -27,18 +27,12 @@ class FilePickerPlugin extends FilePickerPlatform { /// Load file from user's computer and return it as an XFile /// TODO: multiple files Future> loadFile({List acceptedTypes}) { - String inputString = ''; - for (String element in acceptedTypes) { - if (inputString.isNotEmpty) { - inputString += ','; - } - inputString += element; - } + String acceptedTypeString = acceptedTypes.where((e) => e.isNotEmpty).join(','); // Create a file input element - html.FileUploadInputElement element = html.FileUploadInputElement(); - if (inputString.isNotEmpty) { - element.accept = inputString; + final FileUploadInputElement element = FileUploadInputElement(); + if (acceptedTypeString.isNotEmpty) { + element.accept = acceptedTypeString; } element.multiple = true; @@ -47,17 +41,17 @@ class FilePickerPlugin extends FilePickerPlatform { _target.children.add(element); element.click(); - Completer> _completer = Completer>(); + final Completer> _completer = Completer(); // Get the returned files // TODO: Handle errors element.onChange.first.then((event) { - // TODO: Multiple files - List files = element.files; + // File type from dart:html class + List files = element.files; List returnFiles = List(); - for (html.File file in files) { - String url = html.Url.createObjectUrl(file); + for (File file in files) { + String url = Url.createObjectUrl(file); String name = file.name; int length = file.size; @@ -76,16 +70,13 @@ class FilePickerPlugin extends FilePickerPlatform { void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { // Create blob from data // TODO: Handle different types - html.Blob blob; - if(type.isEmpty) { - blob = html.Blob([data]); - } else { - blob = html.Blob([data], type); - } - String url = html.Url.createObjectUrl(blob); + + final Blob blob = type.isEmpty ? Blob([data]) : Blob([data]); + + String url = Url.createObjectUrl(blob); // Create an tag with the appropriate download attributes and click it - html.AnchorElement element = html.AnchorElement( + final AnchorElement element = AnchorElement( href: url, ); element.download = suggestedName; @@ -95,13 +86,13 @@ class FilePickerPlugin extends FilePickerPlatform { } /// Initializes a DOM container where we can host input elements. - html.Element _ensureInitialized(String id) { - var target = html.querySelector('#${id}'); + Element _ensureInitialized(String id) { + var target = querySelector('#${id}'); if (target == null) { - final html.Element targetElement = - html.Element.tag('flt-image-picker-inputs')..id = id; + final Element targetElement = + Element.tag('flt-image-picker-inputs')..id = id; - html.querySelector('body').children.add(targetElement); + querySelector('body').children.add(targetElement); target = targetElement; } return target; From 2b21ba43855a50a222939eabb795bf7a43f9937b Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 27 Jul 2020 16:54:57 -0700 Subject: [PATCH 012/151] Basic Method Channel Tests --- .../pubspec.yaml | 1 + .../test/method_channel_file_picker.dart | 11 ----- .../test/method_channel_file_picker_test.dart | 47 +++++++++++++++++++ .../file_picker_web/lib/file_picker_web.dart | 2 +- 4 files changed, 49 insertions(+), 12 deletions(-) delete mode 100644 packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart create mode 100644 packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker_test.dart diff --git a/packages/file_picker/file_picker_platform_interface/pubspec.yaml b/packages/file_picker/file_picker_platform_interface/pubspec.yaml index 2cca9d6d7a04..249d9f1f9a89 100644 --- a/packages/file_picker/file_picker_platform_interface/pubspec.yaml +++ b/packages/file_picker/file_picker_platform_interface/pubspec.yaml @@ -13,6 +13,7 @@ dependencies: plugin_platform_interface: ^1.0.1 dev_dependencies: + test: ^1.15.0 flutter_test: sdk: flutter mockito: ^4.1.1 diff --git a/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart deleted file mode 100644 index b5670b96635e..000000000000 --- a/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:mockito/mockito.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -void main() { -} \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker_test.dart b/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker_test.dart new file mode 100644 index 000000000000..fd5dd8deea2a --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker_test.dart @@ -0,0 +1,47 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:mockito/mockito.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; +import 'package:file_picker_platform_interface/src/method_channel/method_channel_file_picker.dart'; + +void main() { + group('$FilePickerPlatform', () { + test('$MethodChannelFilePicker() is the default instance', () { + expect(FilePickerPlatform.instance, + isInstanceOf()); + }); + + test('Cannot be implemented with `implements`', () { + expect(() { + FilePickerPlatform.instance = ImplementsFilePickerPlatform(); + }, throwsA(isInstanceOf())); + }); + + test('Can be mocked with `implements`', () { + final FilePickerPlatformMock mock = FilePickerPlatformMock(); + FilePickerPlatform.instance = mock; + }); + + test('Can be extended', () { + FilePickerPlatform.instance = ExtendsFilePickerPlatform(); + }); + }); + + +} + + +class FilePickerPlatformMock extends Mock + with MockPlatformInterfaceMixin + implements FilePickerPlatform {} + +class ImplementsFilePickerPlatform extends Mock + implements FilePickerPlatform {} + +class ExtendsFilePickerPlatform extends FilePickerPlatform {} \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 281e2b49ed15..497dafb85d8c 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -90,7 +90,7 @@ class FilePickerPlugin extends FilePickerPlatform { var target = querySelector('#${id}'); if (target == null) { final Element targetElement = - Element.tag('flt-image-picker-inputs')..id = id; + Element.tag('flt-file-picker-inputs')..id = id; querySelector('body').children.add(targetElement); target = targetElement; From 9b7d3fee8f1bcd328618ba540a8ef8c513a53741 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 27 Jul 2020 17:23:34 -0700 Subject: [PATCH 013/151] Break Down loadFile Into Smaller Parts --- .../file_picker_web/lib/file_picker_web.dart | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 497dafb85d8c..83421736d12c 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -24,22 +24,30 @@ class FilePickerPlugin extends FilePickerPlatform { FilePickerPlatform.instance = FilePickerPlugin(); } - /// Load file from user's computer and return it as an XFile - /// TODO: multiple files - Future> loadFile({List acceptedTypes}) { - String acceptedTypeString = acceptedTypes.where((e) => e.isNotEmpty).join(','); - - // Create a file input element + FileUploadInputElement _createFileInputElement(String accepted) { final FileUploadInputElement element = FileUploadInputElement(); - if (acceptedTypeString.isNotEmpty) { - element.accept = acceptedTypeString; + if (accepted.isNotEmpty) { + element.accept = accepted; } element.multiple = true; + return element; + } + + void _addElementToDomAndClick(Element element) { // Add the file input element and click it _target.children.clear(); _target.children.add(element); element.click(); + } + + /// Load file from user's computer and return it as an XFile + Future> loadFile({List acceptedTypes = const []}) { + String acceptedTypeString = acceptedTypes?.where((e) => e.isNotEmpty)?.join(',') ?? ''; + + final FileUploadInputElement element = _createFileInputElement(acceptedTypeString); + + _addElementToDomAndClick(element); final Completer> _completer = Completer(); @@ -58,7 +66,6 @@ class FilePickerPlugin extends FilePickerPlatform { returnFiles.add(XFile(url, name: name, length: length)); } - _completer.complete(returnFiles); }); @@ -71,18 +78,15 @@ class FilePickerPlugin extends FilePickerPlatform { // Create blob from data // TODO: Handle different types - final Blob blob = type.isEmpty ? Blob([data]) : Blob([data]); + final Blob blob = type.isEmpty ? Blob([data]) : Blob([data], type); String url = Url.createObjectUrl(blob); // Create an tag with the appropriate download attributes and click it - final AnchorElement element = AnchorElement( - href: url, - ); + final AnchorElement element = AnchorElement(href: url); element.download = suggestedName; - _target.children.clear(); - _target.children.add(element); - element.click(); + + _addElementToDomAndClick(element); } /// Initializes a DOM container where we can host input elements. From 5f5f9aa23be349d42277841bf51fa9e00a17541b Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 28 Jul 2020 14:14:29 -0700 Subject: [PATCH 014/151] Use FileTypeFilterGroup class --- .../file_picker/example/lib/main.dart | 6 ++-- .../file_picker/lib/file_picker.dart | 4 +-- .../method_channel_file_picker.dart | 2 +- .../file_picker_interface.dart | 2 +- .../src/types/filter_group/filter_group.dart | 28 +++++++++++++++++++ .../lib/src/types/types.dart | 2 ++ .../file_picker_web/lib/file_picker_web.dart | 10 +++++-- 7 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index c0aa37d24023..5f4ab99e2ef6 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -3,8 +3,6 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:file_picker/file_picker.dart'; - - void main() { runApp(MyApp()); } @@ -64,7 +62,9 @@ class _MyHomePageState extends State { void _loadFile() async { List file; if (_extensionController.text.isNotEmpty) { - file = await loadFile(acceptedTypes: _extensionController.text.split(',')); + List type = List(); + type.add(FileTypeFilterGroup(label: 'Example Files', fileExtensions: _extensionController.text.split(','))); + file = await loadFile(acceptedTypes: type); } else { file = await loadFile(); } diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index e41862beded3..5e10892fe44b 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -8,7 +8,7 @@ import 'dart:typed_data'; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; export 'package:file_picker_platform_interface/file_picker_platform_interface.dart' - show XFile; + show XFile, FileTypeFilterGroup; /// Saves File to user's file system void saveFile(Uint8List data, {String type = '', String suggestedName}) async { @@ -16,6 +16,6 @@ void saveFile(Uint8List data, {String type = '', String suggestedName}) async { } /// Loads File from user's file system -Future> loadFile({List acceptedTypes}) { +Future> loadFile({List acceptedTypes}) { return FilePickerPlatform.instance.loadFile(acceptedTypes: acceptedTypes); } \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart index 89a33a18d0d9..3e90f4dad471 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart @@ -15,7 +15,7 @@ const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); class MethodChannelFilePicker extends FilePickerPlatform { /// Load file from user's computer and return it as an XFile @override - Future> loadFile({List acceptedTypes}) { + Future> loadFile({List acceptedTypes}) { return _channel.invokeMethod>( 'loadFile', { diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index e089870b1741..cfe5a437ea29 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -38,7 +38,7 @@ abstract class FilePickerPlatform extends PlatformInterface { } /// Load file from user's computer and return it as an XFile - Future> loadFile({List acceptedTypes}) { + Future> loadFile({List acceptedTypes}) { throw UnimplementedError('loadFile() has not been implemented.'); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart new file mode 100644 index 000000000000..186e1eca42b3 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart @@ -0,0 +1,28 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// A set of allowed file types. +class FileTypeFilterGroup { + /// Creates a new group with the given label and file extensions. + const FileTypeFilterGroup({this.label, this.fileExtensions}); + + /// The label for the grouping. On platforms that support selectable groups, + /// this will be visible to the user for selecting the group. + final String label; + + /// A list of allowed file extensions. E.g., ['png', 'jpg', 'jpeg', 'gif']. + /// + /// A null or empty list indicates any type is allowed. + final List fileExtensions; +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart index 9b275ed22e97..24e7e7ff83dd 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart @@ -4,6 +4,8 @@ export 'lost_data_response.dart'; export 'retrieve_type.dart'; export 'x_file/x_file.dart'; +export 'filter_group/filter_group.dart'; + /// Denotes that an image is being picked. const String kTypeImage = 'image'; diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 83421736d12c..c1db2ce309db 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -42,15 +42,19 @@ class FilePickerPlugin extends FilePickerPlatform { } /// Load file from user's computer and return it as an XFile - Future> loadFile({List acceptedTypes = const []}) { - String acceptedTypeString = acceptedTypes?.where((e) => e.isNotEmpty)?.join(',') ?? ''; + Future> loadFile({List acceptedTypes = const []}) { + List allExtensions = List(); + for (FileTypeFilterGroup group in acceptedTypes) { + allExtensions += group.fileExtensions; + } + String acceptedTypeString = allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; final FileUploadInputElement element = _createFileInputElement(acceptedTypeString); _addElementToDomAndClick(element); final Completer> _completer = Completer(); - + // Get the returned files // TODO: Handle errors element.onChange.first.then((event) { From a73f088dbb4c3450de78a8ce17c479aadd8bba1e Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 28 Jul 2020 16:55:23 -0700 Subject: [PATCH 015/151] Bug Fix (No Input to File Extension) --- ...est.dart => file_picker_platform_interface_test.dart} | 0 .../file_picker/file_picker_web/lib/file_picker_web.dart | 4 ++-- .../file_picker_web/test/file_picker_web_test.dart | 9 +++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) rename packages/file_picker/file_picker_platform_interface/test/{method_channel_file_picker_test.dart => file_picker_platform_interface_test.dart} (100%) diff --git a/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker_test.dart b/packages/file_picker/file_picker_platform_interface/test/file_picker_platform_interface_test.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker_test.dart rename to packages/file_picker/file_picker_platform_interface/test/file_picker_platform_interface_test.dart diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index c1db2ce309db..5dea1ad162ab 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -42,9 +42,9 @@ class FilePickerPlugin extends FilePickerPlatform { } /// Load file from user's computer and return it as an XFile - Future> loadFile({List acceptedTypes = const []}) { + Future> loadFile({List acceptedTypes}) { List allExtensions = List(); - for (FileTypeFilterGroup group in acceptedTypes) { + for (FileTypeFilterGroup group in acceptedTypes ?? []) { allExtensions += group.fileExtensions; } String acceptedTypeString = allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; diff --git a/packages/file_picker/file_picker_web/test/file_picker_web_test.dart b/packages/file_picker/file_picker_web/test/file_picker_web_test.dart index d471ab5e3446..97902b475071 100644 --- a/packages/file_picker/file_picker_web/test/file_picker_web_test.dart +++ b/packages/file_picker/file_picker_web/test/file_picker_web_test.dart @@ -4,6 +4,7 @@ @TestOn('chrome') // Uses web-only Flutter SDK +import 'dart:typed_data'; import 'dart:html' as html; import 'package:flutter_test/flutter_test.dart'; import 'package:file_picker_web/file_picker_web.dart'; @@ -14,4 +15,12 @@ import 'package:platform_detect/test_utils.dart' as platform; class MockWindow extends Mock implements html.Window {} void main() { + // Under test.. + FilePicker plugin; + + setUp(() { + plugin = FilePicker(); + }); + + } \ No newline at end of file From e8e9f418a954cb4648c955166d2ae3490f91316a Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 28 Jul 2020 17:27:29 -0700 Subject: [PATCH 016/151] More Refactor of loadFile and saveFile into Smaller Pieces (Prep for Testing) --- .../file_picker_web/lib/file_picker_web.dart | 63 +++++++++++++------ 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 5dea1ad162ab..73f54588b10f 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -36,32 +36,20 @@ class FilePickerPlugin extends FilePickerPlatform { void _addElementToDomAndClick(Element element) { // Add the file input element and click it + // All previous elements will be removed before adding the new one _target.children.clear(); _target.children.add(element); element.click(); } - /// Load file from user's computer and return it as an XFile - Future> loadFile({List acceptedTypes}) { - List allExtensions = List(); - for (FileTypeFilterGroup group in acceptedTypes ?? []) { - allExtensions += group.fileExtensions; - } - String acceptedTypeString = allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; - - final FileUploadInputElement element = _createFileInputElement(acceptedTypeString); - - _addElementToDomAndClick(element); - - final Completer> _completer = Completer(); - - // Get the returned files - // TODO: Handle errors + Future> _getFileFromInputElement(InputElement element) { + // Listens for element change element.onChange.first.then((event) { // File type from dart:html class List files = element.files; List returnFiles = List(); + // Create XFiles from dart:html Files for (File file in files) { String url = Url.createObjectUrl(file); String name = file.name; @@ -72,10 +60,40 @@ class FilePickerPlugin extends FilePickerPlatform { _completer.complete(returnFiles); }); - + + element.onError.first.then((event) { + if (!_completer.isCompleted) { + _completer.completeError(event); + } + }); + return _completer.future; } + /// Load file from user's computer and return it as an XFile + @override + Future> loadFile({List acceptedTypes}) { + List allExtensions = List(); + for (FileTypeFilterGroup group in acceptedTypes ?? []) { + allExtensions += group.fileExtensions; + } + String acceptedTypeString = allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; + + final FileUploadInputElement element = _createFileInputElement(acceptedTypeString); + + _addElementToDomAndClick(element); + + final Completer> _completer = Completer(); + + return _getFileFromInputElement(element); + } + + AnchorElement _createAnchorElement(String href, String suggestedName) { + final AnchorElement element = AnchorElement(href: url); + element.download = suggestedName; + return element; + } + /// Web implementation of saveFile() @override void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { @@ -87,13 +105,12 @@ class FilePickerPlugin extends FilePickerPlatform { String url = Url.createObjectUrl(blob); // Create an tag with the appropriate download attributes and click it - final AnchorElement element = AnchorElement(href: url); - element.download = suggestedName; + final AnchorElement element = _createAnchorElement(href: url, suggestedName: suggestedName); _addElementToDomAndClick(element); } - /// Initializes a DOM container where we can host input elements. + /// Initializes a DOM container where we can host elements. Element _ensureInitialized(String id) { var target = querySelector('#${id}'); if (target == null) { @@ -106,3 +123,9 @@ class FilePickerPlugin extends FilePickerPlatform { return target; } } + +/// Overrides some functions to allow testing +@visibleForTesting +class FilePickerPluginTestOverrides { + +} \ No newline at end of file From 2dc5059038249aea6f9a51a282d402defbfd5709 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Thu, 30 Jul 2020 17:45:21 -0700 Subject: [PATCH 017/151] Initial tests for web implementation Also some refactoring of plugin code to accomplish tests --- .../file_picker_web/lib/file_picker_web.dart | 89 ++++++++++++++----- .../file_picker_web/test/README.md | 17 ++++ .../test/file_picker_web_test.dart | 46 ++++++++-- .../file_picker_web/test/lib/main.dart | 22 +++++ .../file_picker_web/test/pubspec.yaml | 23 +++++ .../test/test_driver/web_e2e.dart | 50 +++++++++++ .../test/test_driver/web_e2e_test.dart | 7 ++ .../file_picker_web/test/web/index.html | 13 +++ 8 files changed, 238 insertions(+), 29 deletions(-) create mode 100644 packages/file_picker/file_picker_web/test/README.md create mode 100644 packages/file_picker/file_picker_web/test/lib/main.dart create mode 100644 packages/file_picker/file_picker_web/test/pubspec.yaml create mode 100644 packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart create mode 100644 packages/file_picker/file_picker_web/test/test_driver/web_e2e_test.dart create mode 100644 packages/file_picker/file_picker_web/test/web/index.html diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 73f54588b10f..ada730e1932a 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:meta/meta.dart'; final String _kFilePickerInputsDomId = '__file_picker_web-file-input'; @@ -12,10 +13,15 @@ final String _kFilePickerInputsDomId = '__file_picker_web-file-input'; /// This class implements the `package:file_picker` functionality for the web. class FilePickerPlugin extends FilePickerPlatform { Element _target; - - /// Default constructor, initializes _target to a DOM element - /// that we can use to host HTML elements - FilePickerPlugin() { + final FilePickerPluginTestOverrides _overrides; + bool get _hasTestOverrides => _overrides != null; + + /// Default constructor, initializes _target to a DOM element that we can use + /// to host HTML elements. + /// overrides parameter allows for testing to override functions + FilePickerPlugin({ + @visibleForTesting FilePickerPluginTestOverrides overrides, + }) : _overrides = overrides { _target = _ensureInitialized(_kFilePickerInputsDomId); } @@ -23,8 +29,23 @@ class FilePickerPlugin extends FilePickerPlatform { static void registerWith(Registrar registrar) { FilePickerPlatform.instance = FilePickerPlugin(); } + + /// Convert list of filter groups to a comma-separated string + String _getStringFromFilterGroup (List acceptedTypes) { + List allExtensions = List(); + for (FileTypeFilterGroup group in acceptedTypes ?? []) { + allExtensions += group.fileExtensions; + } + return allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; + } - FileUploadInputElement _createFileInputElement(String accepted) { + /// Creates a file input element with only the accept attribute + @visibleForTesting + FileUploadInputElement createFileInputElement(String accepted) { + if (_hasTestOverrides) { + return _overrides.createFileInputElement(accepted); + } + final FileUploadInputElement element = FileUploadInputElement(); if (accepted.isNotEmpty) { element.accept = accepted; @@ -42,11 +63,39 @@ class FilePickerPlugin extends FilePickerPlatform { element.click(); } - Future> _getFileFromInputElement(InputElement element) { + List _getXFilesFromFiles (List files) { + List xFiles = List(); + + for (File file in files) { + String url = Url.createObjectUrl(file); + String name = file.name; + int length = file.size; + + xFiles.add(XFile(url, name: name, length: length)); + } + + return xFiles; + } + + /// Getter for retrieving files from an input element + @visibleForTesting + List getFilesFromInputElement(InputElement element) { + if(_hasTestOverrides) { + return _overrides.getFilesFromInputElement(element); + } + + return element?.files ?? []; + } + + /// Listen for file input element to change and retrieve files when + /// this happens. + Future> _getFilesWhenReady(InputElement element) { + final Completer> _completer = Completer(); + // Listens for element change element.onChange.first.then((event) { // File type from dart:html class - List files = element.files; + List files = getFilesFromInputElement(element); List returnFiles = List(); // Create XFiles from dart:html Files @@ -73,23 +122,17 @@ class FilePickerPlugin extends FilePickerPlatform { /// Load file from user's computer and return it as an XFile @override Future> loadFile({List acceptedTypes}) { - List allExtensions = List(); - for (FileTypeFilterGroup group in acceptedTypes ?? []) { - allExtensions += group.fileExtensions; - } - String acceptedTypeString = allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; + final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); - final FileUploadInputElement element = _createFileInputElement(acceptedTypeString); + final FileUploadInputElement element = createFileInputElement(acceptedTypeString); _addElementToDomAndClick(element); - - final Completer> _completer = Completer(); - - return _getFileFromInputElement(element); + + return _getFilesWhenReady(element); } AnchorElement _createAnchorElement(String href, String suggestedName) { - final AnchorElement element = AnchorElement(href: url); + final AnchorElement element = AnchorElement(href: href); element.download = suggestedName; return element; } @@ -98,14 +141,12 @@ class FilePickerPlugin extends FilePickerPlatform { @override void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { // Create blob from data - // TODO: Handle different types - final Blob blob = type.isEmpty ? Blob([data]) : Blob([data], type); String url = Url.createObjectUrl(blob); // Create an tag with the appropriate download attributes and click it - final AnchorElement element = _createAnchorElement(href: url, suggestedName: suggestedName); + final AnchorElement element = _createAnchorElement(url, suggestedName); _addElementToDomAndClick(element); } @@ -127,5 +168,9 @@ class FilePickerPlugin extends FilePickerPlatform { /// Overrides some functions to allow testing @visibleForTesting class FilePickerPluginTestOverrides { - + /// For overriding the creation of the file input element. + Element Function(String accepted) createFileInputElement; + + /// For overriding retrieving a file from the input element. + List Function(InputElement input) getFilesFromInputElement; } \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/test/README.md b/packages/file_picker/file_picker_web/test/README.md new file mode 100644 index 000000000000..f623e9fd08d3 --- /dev/null +++ b/packages/file_picker/file_picker_web/test/README.md @@ -0,0 +1,17 @@ +# Running browser_tests + +Make sure you have updated to the latest Flutter master. + +1. Check what version of Chrome is running on the machine you're running tests on. + +2. Download and install driver for that version from here: + * + +3. Start the driver using `chromedriver --port=4444` + +4. Change into the `test` directory of your clone. + +5. Run tests: `flutter drive -d web-server --release --browser-name=chrome --target=test_driver/TEST_NAME_e2e.dart`, or (in Linux): + + * Single: `./run_test test_driver/TEST_NAME_e2e.dart` + * All: `./run_test` diff --git a/packages/file_picker/file_picker_web/test/file_picker_web_test.dart b/packages/file_picker/file_picker_web/test/file_picker_web_test.dart index 97902b475071..830bb19b9be3 100644 --- a/packages/file_picker/file_picker_web/test/file_picker_web_test.dart +++ b/packages/file_picker/file_picker_web/test/file_picker_web_test.dart @@ -2,25 +2,57 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@TestOn('chrome') // Uses web-only Flutter SDK +//@TestOn('chrome') // Uses web-only Flutter SDK +import 'dart:convert'; import 'dart:typed_data'; -import 'dart:html' as html; +import 'dart:html'; + import 'package:flutter_test/flutter_test.dart'; import 'package:file_picker_web/file_picker_web.dart'; -import 'package:mockito/mockito.dart'; +import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; import 'package:platform_detect/test_utils.dart' as platform; -class MockWindow extends Mock implements html.Window {} +final String expectedStringContents = 'Hello, world!'; +final expectedSize = expectedStringContents.length; +final Uint8List bytes = utf8.encode(expectedStringContents); +final File textFile = File([bytes], 'hello.txt'); void main() { // Under test.. - FilePicker plugin; + FilePickerPlugin plugin; setUp(() { - plugin = FilePicker(); + plugin = FilePickerPlugin(); + }); + + test('Basic', () { expect(1+1, 2); }); +/* + test('Select a file to load', () async { + final mockInput = FileUploadInputElement(); + + final plugin = ImagePickerPlugin( + overrides: FilePickerPluginTestOverrides() + ..createFileInputElement((_) => mockInput) + ..getFileFromInputElement((_) => textFile) + ); + + // Call load file + final XFile file = plugin.loadFile(); + + // Mock selection of a file + mockInput.dispatchEvent(Event('change')); + + // Expect the file to complete + expect(file, completes); + + // Expect that we can read from the file + final loadedFile = await file; + expect(loadedFile.readAsBytes(), completion(isNotEmpty)); + expect(pickedFile.length(), completion(equals(expectedSize))); + expect(loadedFile.name, 'hello.txt'); }); - + */ } \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/test/lib/main.dart b/packages/file_picker/file_picker_web/test/lib/main.dart new file mode 100644 index 000000000000..4c55f6c95a7a --- /dev/null +++ b/packages/file_picker/file_picker_web/test/lib/main.dart @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatefulWidget { + @override + MyAppState createState() => MyAppState(); +} + +class MyAppState extends State { + @override + Widget build(BuildContext context) { + return Text('Testing... Look at the console output for results!'); + } +} + diff --git a/packages/file_picker/file_picker_web/test/pubspec.yaml b/packages/file_picker/file_picker_web/test/pubspec.yaml new file mode 100644 index 000000000000..c59457c1d9d6 --- /dev/null +++ b/packages/file_picker/file_picker_web/test/pubspec.yaml @@ -0,0 +1,23 @@ +name: regular_integration_tests +publish_to: none + +environment: + sdk: ">=2.2.2 <3.0.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + file_picker_web: + path: ../ + file_picker_platform_interface: + path: ../../file_picker_platform_interface + google_maps: ^3.4.4 + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + e2e: ^0.6.1 + http: ^0.12.2 + mockito: ^4.1.1 \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart b/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart new file mode 100644 index 000000000000..44290a37cd2f --- /dev/null +++ b/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart @@ -0,0 +1,50 @@ +import 'dart:async'; + +import 'package:e2e/e2e.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + + +import 'dart:convert'; +import 'dart:typed_data'; +import 'dart:html'; + +import 'package:file_picker_web/file_picker_web.dart'; +import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; + +import 'package:platform_detect/test_utils.dart' as platform; + +final String expectedStringContents = 'Hello, world!'; +final expectedSize = expectedStringContents.length; +final Uint8List bytes = utf8.encode(expectedStringContents); +final File textFile = File([bytes], 'hello.txt'); + +/// Test Markers +void main() { + E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; + + test('Select a single file to load', () async { + final mockInput = FileUploadInputElement(); + + final plugin = FilePickerPlugin( + overrides: FilePickerPluginTestOverrides() + ..createFileInputElement = ((_) => mockInput) + ..getFilesFromInputElement = ((_) => [textFile]) + ); + + // Call load file + final files = plugin.loadFile(); + // Mock selection of a file + mockInput.dispatchEvent(Event('change')); + + // Expect the file to complete + expect(files, completes); + + // Expect that we can read from the file + final loadedFiles = await files; + final loadedFile = loadedFiles.first; + expect(loadedFile.readAsBytes(), completion(isNotEmpty)); + expect(loadedFile.length(), completion(equals(expectedSize))); + expect(loadedFile.name, 'hello.txt'); + }); +} diff --git a/packages/file_picker/file_picker_web/test/test_driver/web_e2e_test.dart b/packages/file_picker/file_picker_web/test/test_driver/web_e2e_test.dart new file mode 100644 index 000000000000..a29203f7dcdd --- /dev/null +++ b/packages/file_picker/file_picker_web/test/test_driver/web_e2e_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:e2e/e2e_driver.dart' as e2e; + +Future main() async => e2e.main(); diff --git a/packages/file_picker/file_picker_web/test/web/index.html b/packages/file_picker/file_picker_web/test/web/index.html new file mode 100644 index 000000000000..59a832b5de4c --- /dev/null +++ b/packages/file_picker/file_picker_web/test/web/index.html @@ -0,0 +1,13 @@ + + + + + Browser Tests + + + + + + From 73c4b62266db08444f8669dfd20b6302eefd3d68 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 31 Jul 2020 15:25:04 -0700 Subject: [PATCH 018/151] Delete original test file --- .../test/file_picker_web_test.dart | 58 ------------------- 1 file changed, 58 deletions(-) delete mode 100644 packages/file_picker/file_picker_web/test/file_picker_web_test.dart diff --git a/packages/file_picker/file_picker_web/test/file_picker_web_test.dart b/packages/file_picker/file_picker_web/test/file_picker_web_test.dart deleted file mode 100644 index 830bb19b9be3..000000000000 --- a/packages/file_picker/file_picker_web/test/file_picker_web_test.dart +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//@TestOn('chrome') // Uses web-only Flutter SDK - -import 'dart:convert'; -import 'dart:typed_data'; -import 'dart:html'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:file_picker_web/file_picker_web.dart'; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; - -import 'package:platform_detect/test_utils.dart' as platform; - -final String expectedStringContents = 'Hello, world!'; -final expectedSize = expectedStringContents.length; -final Uint8List bytes = utf8.encode(expectedStringContents); -final File textFile = File([bytes], 'hello.txt'); - -void main() { - // Under test.. - FilePickerPlugin plugin; - - setUp(() { - plugin = FilePickerPlugin(); - }); - - test('Basic', () { expect(1+1, 2); }); -/* - test('Select a file to load', () async { - final mockInput = FileUploadInputElement(); - - final plugin = ImagePickerPlugin( - overrides: FilePickerPluginTestOverrides() - ..createFileInputElement((_) => mockInput) - ..getFileFromInputElement((_) => textFile) - ); - - // Call load file - final XFile file = plugin.loadFile(); - - // Mock selection of a file - mockInput.dispatchEvent(Event('change')); - - // Expect the file to complete - expect(file, completes); - - // Expect that we can read from the file - final loadedFile = await file; - expect(loadedFile.readAsBytes(), completion(isNotEmpty)); - expect(pickedFile.length(), completion(equals(expectedSize))); - expect(loadedFile.name, 'hello.txt'); - }); - - */ -} \ No newline at end of file From 3b3ab630c1abc0775a8ff516e090b236aca75e3e Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 31 Jul 2020 17:27:33 -0700 Subject: [PATCH 019/151] Test Loading Multiple Files + saveFile tests --- .../file_picker_web/lib/file_picker_web.dart | 17 ++- .../test/test_driver/web_e2e.dart | 116 ++++++++++++++---- 2 files changed, 105 insertions(+), 28 deletions(-) diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index ada730e1932a..f03663c16896 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -131,22 +131,29 @@ class FilePickerPlugin extends FilePickerPlatform { return _getFilesWhenReady(element); } - AnchorElement _createAnchorElement(String href, String suggestedName) { + /// Create anchor element with download attribute + @visibleForTesting + AnchorElement createAnchorElement(String href, String suggestedName) { final AnchorElement element = AnchorElement(href: href); element.download = suggestedName; return element; } + /// Create blob with specified data of indicated type + @visibleForTesting + Blob createBlob(Uint8List data, String type) { + return type.isEmpty ? Blob([data]) : Blob([data], type); + } + /// Web implementation of saveFile() @override void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { // Create blob from data - final Blob blob = type.isEmpty ? Blob([data]) : Blob([data], type); - + final blob = createBlob(data, type); String url = Url.createObjectUrl(blob); // Create an tag with the appropriate download attributes and click it - final AnchorElement element = _createAnchorElement(url, suggestedName); + final AnchorElement element = createAnchorElement(url, suggestedName); _addElementToDomAndClick(element); } @@ -170,7 +177,7 @@ class FilePickerPlugin extends FilePickerPlatform { class FilePickerPluginTestOverrides { /// For overriding the creation of the file input element. Element Function(String accepted) createFileInputElement; - + /// For overriding retrieving a file from the input element. List Function(InputElement input) getFilesFromInputElement; } \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart b/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart index 44290a37cd2f..6442507f0f6e 100644 --- a/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart +++ b/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart @@ -19,32 +19,102 @@ final expectedSize = expectedStringContents.length; final Uint8List bytes = utf8.encode(expectedStringContents); final File textFile = File([bytes], 'hello.txt'); +final String expectedStringContents2 = 'This is the other test file'; +final expectedSize2 = expectedStringContents.length; +final Uint8List bytes2 = utf8.encode(expectedStringContents); +final File textFile2 = File([bytes], 'test2.txt'); + + /// Test Markers void main() { E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; - test('Select a single file to load', () async { - final mockInput = FileUploadInputElement(); - - final plugin = FilePickerPlugin( - overrides: FilePickerPluginTestOverrides() - ..createFileInputElement = ((_) => mockInput) - ..getFilesFromInputElement = ((_) => [textFile]) - ); - - // Call load file - final files = plugin.loadFile(); - // Mock selection of a file - mockInput.dispatchEvent(Event('change')); - - // Expect the file to complete - expect(files, completes); - - // Expect that we can read from the file - final loadedFiles = await files; - final loadedFile = loadedFiles.first; - expect(loadedFile.readAsBytes(), completion(isNotEmpty)); - expect(loadedFile.length(), completion(equals(expectedSize))); - expect(loadedFile.name, 'hello.txt'); + group('loadFile: ', () { + test('Select a single file to load', () async { + final mockInput = FileUploadInputElement(); + + // Note that we override the retrieval of files from the input element. + // We opt to do this because dart cannot edit the "files" attribute of + // tag. When performing mockInput.files = [ textFile ] we receive: + // "Failed to set the 'files' property on 'HTMLInputElement': The provided + // value is not of type 'FileList'." + // + // More on this (javascript side): https://stackoverflow.com/questions/52078853/is-it-possible-to-update-filelist + // The dart implementation will depend on the javascript it creates. + final plugin = FilePickerPlugin( + overrides: FilePickerPluginTestOverrides() + ..createFileInputElement = ((_) => mockInput) + ..getFilesFromInputElement = ((_) => [textFile]) + ); + + // Call load file + final files = plugin.loadFile(); + + // Mock selection of a file + mockInput.dispatchEvent(Event('change')); + + // Expect the file to complete + expect(files, completes); + + // Expect that we can read from the file + final loadedFiles = await files; + final loadedFile = loadedFiles.first; + expect(loadedFile.readAsBytes(), completion(isNotEmpty)); + expect(loadedFile.length(), completion(equals(expectedSize))); + expect(loadedFile.name, textFile.name); + }); + + test('Select multiple files to load', () async { + final mockInput = FileUploadInputElement(); + + final plugin = FilePickerPlugin( + overrides: FilePickerPluginTestOverrides() + ..createFileInputElement = ((_) => mockInput) + ..getFilesFromInputElement = ((_) => [textFile, textFile2]) + ); + + // Call load file + final files = plugin.loadFile(); + + // Mock selection of a file + mockInput.dispatchEvent(Event('change')); + + // Expect the file to complete + expect(files, completes); + + // Expect that we can read from the files + final loadedFiles = await files; + final file1 = loadedFiles[0]; + expect(file1.readAsBytes(), completion(isNotEmpty)); + expect(file1.length(), completion(equals(expectedSize))); + expect(file1.name, textFile.name); + + final file2 = loadedFiles[1]; + expect(file2.readAsBytes(), completion(isNotEmpty)); + expect(file2.length(), completion(equals(expectedSize2))); + expect(file2.name, textFile2.name); + }); + }); + + group('saveFile: ', () { + test('Create a blob', () { + Uint8List data = Uint8List.fromList(expectedStringContents.codeUnits); + + FilePickerPlugin plugin = FilePickerPlugin(); + final blob = plugin.createBlob(data, 'text/plain'); + + expect(blob.type, 'text/plain'); + expect(blob.size, expectedSize); + }); + + test('Create an anchor', () { + FilePickerPlugin plugin = FilePickerPlugin(); + final String href = 'https://google.com'; + final String name = 'file_name.txt'; + final anchor = plugin.createAnchorElement(href, name); + + expect(anchor.download, name); + expect(anchor.href, href); + }); }); } From d24b55a1f4fb3d72e005365dda449843b859172b Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 4 Aug 2020 11:27:32 -0700 Subject: [PATCH 020/151] Fix Example Text Overflow --- packages/file_picker/file_picker/example/lib/main.dart | 5 ++++- packages/file_picker/file_picker/example/pubspec.yaml | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index 5f4ab99e2ef6..500a4b7c073c 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -92,6 +92,8 @@ class _MyHomePageState extends State { Container( width: 300, child: TextField( + minLines: 1, + maxLines: 12, controller: _nameController, decoration: InputDecoration( hintText: '(Optional) Suggest File Name', @@ -101,7 +103,8 @@ class _MyHomePageState extends State { Container( width: 300, child: TextField( - maxLines: null, + minLines: 1, + maxLines: 12, controller: _fileController, decoration: InputDecoration( hintText: 'Enter File Contents', diff --git a/packages/file_picker/file_picker/example/pubspec.yaml b/packages/file_picker/file_picker/example/pubspec.yaml index baa9292c63cc..c292d1b300aa 100644 --- a/packages/file_picker/file_picker/example/pubspec.yaml +++ b/packages/file_picker/file_picker/example/pubspec.yaml @@ -27,7 +27,6 @@ dependencies: file_picker: path: ../ - # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.3 From 727c7528403b830c5e349063bce9189e3d8f878e Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 4 Aug 2020 15:22:40 -0700 Subject: [PATCH 021/151] Add XPath class and New API to interface --- .../file_picker_interface.dart | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index cfe5a437ea29..76a9a5164ec6 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -10,6 +10,20 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import '../method_channel/method_channel_file_picker.dart'; + +// TODO: move to its own file +/// Cross platform path class +class XPath { + /// XPath constructor + XPath(this._path, {int modified, int created}): + _modified = modified, + _created = created; + + final String _path; + final int _modified; + final int _created; +} + /// The interface that implementations of file_picker must implement. /// /// Platform implementations should extend this class rather than implement it as `file_picker` @@ -37,6 +51,24 @@ abstract class FilePickerPlatform extends PlatformInterface { _instance = instance; } + /// Open file dialog for loading files and return a file path + XPath getReadPath() { + throw UnimplementedError('getReadPath() has not been implemented.'); + } + + /// Open file dialog for loading files and return a list of file paths + List getReadPaths() { + throw UnimplementedError('getReadPaths() has not been implemented.'); + } + + /// Open file dialog for saving files and return a file path at which to save + XPath getSavePath() { + throw UnimplementedError('loadFile() has not been implemented.'); + } + + + /// OLD API: + /// Load file from user's computer and return it as an XFile Future> loadFile({List acceptedTypes}) { throw UnimplementedError('loadFile() has not been implemented.'); From d0855d9533f64134e60121eac68afc49e5b0ed43 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 4 Aug 2020 17:02:14 -0700 Subject: [PATCH 022/151] Add XPath and begin getReadPath(s) --- .../file_picker/lib/file_picker.dart | 20 +++ .../file_picker_interface.dart | 19 +-- .../lib/src/types/types.dart | 1 + .../lib/src/types/x_path/x_path.dart | 18 +++ .../file_picker_web/lib/file_picker_web.dart | 115 +++++++++++------- 5 files changed, 114 insertions(+), 59 deletions(-) create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/x_path/x_path.dart diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index 5e10892fe44b..0ae8206f57db 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -10,6 +10,26 @@ import 'package:file_picker_platform_interface/file_picker_platform_interface.da export 'package:file_picker_platform_interface/file_picker_platform_interface.dart' show XFile, FileTypeFilterGroup; +/// NEW API + +/// Open file dialog for loading files and return a file path +XPath getReadPath({List acceptedTypes}) { + return FilePickerPlatform.instance.getReadPath(acceptedTypes: acceptedTypes); +} + +/// Open file dialog for loading files and return a list of file paths +List getReadPaths({List acceptedTypes}) { + return FilePickerPlatform.instance.getReadPaths(acceptedTypes: acceptedTypes); +} + +/// Open file dialog for saving files and return a file path at which to save +XPath getSavePath() { + throw UnimplementedError('loadFile() has not been implemented.'); +} + + +/// OLD API + /// Saves File to user's file system void saveFile(Uint8List data, {String type = '', String suggestedName}) async { return FilePickerPlatform.instance.saveFile(data, type: type, suggestedName: suggestedName); diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index 76a9a5164ec6..2d9fdc1178fd 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -11,18 +11,7 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import '../method_channel/method_channel_file_picker.dart'; -// TODO: move to its own file -/// Cross platform path class -class XPath { - /// XPath constructor - XPath(this._path, {int modified, int created}): - _modified = modified, - _created = created; - - final String _path; - final int _modified; - final int _created; -} + /// The interface that implementations of file_picker must implement. /// @@ -52,17 +41,17 @@ abstract class FilePickerPlatform extends PlatformInterface { } /// Open file dialog for loading files and return a file path - XPath getReadPath() { + Future getReadPath({List acceptedTypes}) { throw UnimplementedError('getReadPath() has not been implemented.'); } /// Open file dialog for loading files and return a list of file paths - List getReadPaths() { + Future> getReadPaths({List acceptedTypes}) { throw UnimplementedError('getReadPaths() has not been implemented.'); } /// Open file dialog for saving files and return a file path at which to save - XPath getSavePath() { + Future getSavePath() { throw UnimplementedError('loadFile() has not been implemented.'); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart index 24e7e7ff83dd..1c7bd02345c5 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart @@ -3,6 +3,7 @@ export 'image_source.dart'; export 'lost_data_response.dart'; export 'retrieve_type.dart'; export 'x_file/x_file.dart'; +export 'x_path/x_path.dart'; export 'filter_group/filter_group.dart'; diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_path/x_path.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_path/x_path.dart new file mode 100644 index 000000000000..07df81e9dce8 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_path/x_path.dart @@ -0,0 +1,18 @@ +/// Cross platform path class +class XPath { + /// XPath constructor + XPath(this._path, {String name, int modified, int created}): + _modified = modified, + _created = created, + _name = name; + + final String _path; + final String _name; + final int _modified; + final int _created; + + String get path => _path; + int get modified => _modified; + int get created => _created; + String get name => _name; +} \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index f03663c16896..24048653bb13 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -41,7 +41,7 @@ class FilePickerPlugin extends FilePickerPlatform { /// Creates a file input element with only the accept attribute @visibleForTesting - FileUploadInputElement createFileInputElement(String accepted) { + FileUploadInputElement createFileInputElement(String accepted, bool multiple) { if (_hasTestOverrides) { return _overrides.createFileInputElement(accepted); } @@ -50,7 +50,7 @@ class FilePickerPlugin extends FilePickerPlatform { if (accepted.isNotEmpty) { element.accept = accepted; } - element.multiple = true; + element.multiple = multiple; return element; } @@ -63,18 +63,19 @@ class FilePickerPlugin extends FilePickerPlatform { element.click(); } - List _getXFilesFromFiles (List files) { - List xFiles = List(); + List _getXPathsFromFiles (List files) { + List xPaths = List(); for (File file in files) { String url = Url.createObjectUrl(file); String name = file.name; int length = file.size; + int modified = file.lastModified; - xFiles.add(XFile(url, name: name, length: length)); + xPaths.add(XPath(url, name: name, modified: modified)); } - return xFiles; + return xPaths; } /// Getter for retrieving files from an input element @@ -87,27 +88,34 @@ class FilePickerPlugin extends FilePickerPlatform { return element?.files ?? []; } + Future _getFileWhenReady(InputElement element) { + final Completer _completer = Completer(); + + _getFilesWhenReady(element) + .then((list) { + _completer.complete(list[0]); + }) + .catchError((err) { + _completer.completeError(err); + }); + + return _completer.future; + } + /// Listen for file input element to change and retrieve files when /// this happens. - Future> _getFilesWhenReady(InputElement element) { + Future> _getFilesWhenReady(InputElement element) { final Completer> _completer = Completer(); // Listens for element change element.onChange.first.then((event) { // File type from dart:html class - List files = getFilesFromInputElement(element); - List returnFiles = List(); + final List files = getFilesFromInputElement(element); - // Create XFiles from dart:html Files - for (File file in files) { - String url = Url.createObjectUrl(file); - String name = file.name; - int length = file.size; - - returnFiles.add(XFile(url, name: name, length: length)); - } + // Create XPath from dart:html Files + final returnPaths = _getXPathsFromFiles(files); - _completer.complete(returnFiles); + _completer.complete(returnPaths); }); element.onError.first.then((event) { @@ -119,22 +127,10 @@ class FilePickerPlugin extends FilePickerPlatform { return _completer.future; } - /// Load file from user's computer and return it as an XFile - @override - Future> loadFile({List acceptedTypes}) { - final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); - - final FileUploadInputElement element = createFileInputElement(acceptedTypeString); - - _addElementToDomAndClick(element); - - return _getFilesWhenReady(element); - } - /// Create anchor element with download attribute @visibleForTesting AnchorElement createAnchorElement(String href, String suggestedName) { - final AnchorElement element = AnchorElement(href: href); + final element = AnchorElement(href: href); element.download = suggestedName; return element; } @@ -145,19 +141,6 @@ class FilePickerPlugin extends FilePickerPlatform { return type.isEmpty ? Blob([data]) : Blob([data], type); } - /// Web implementation of saveFile() - @override - void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { - // Create blob from data - final blob = createBlob(data, type); - String url = Url.createObjectUrl(blob); - - // Create an tag with the appropriate download attributes and click it - final AnchorElement element = createAnchorElement(url, suggestedName); - - _addElementToDomAndClick(element); - } - /// Initializes a DOM container where we can host elements. Element _ensureInitialized(String id) { var target = querySelector('#${id}'); @@ -170,6 +153,50 @@ class FilePickerPlugin extends FilePickerPlatform { } return target; } + + /// NEW API + + // Load Helper + Future> _readPathHelper (bool multiple, List acceptedTypes) { + final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); + + final FileUploadInputElement element = createFileInputElement(acceptedTypeString, multiple); + + _addElementToDomAndClick(element); + + return _getFilesWhenReady(element); + } + + /// Open file dialog for loading files and return a file path + @override + Future getReadPath({List acceptedTypes}) { + return _readPathHelper(false, acceptedTypes); + } + + /// Open file dialog for loading files and return a list of file paths + @override + Future> getReadPaths({List acceptedTypes}) { + return _readPathHelper(true, acceptedTypes); + } + + /// Open file dialog for saving files and return a file path at which to save + @override + Future getSavePath() { + throw UnimplementedError('loadFile() has not been implemented.'); + } + + /// Web implementation of saveFile() + @override + void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { + // Create blob from data + final blob = createBlob(data, type); + String url = Url.createObjectUrl(blob); + + // Create an tag with the appropriate download attributes and click it + final AnchorElement element = createAnchorElement(url, suggestedName); + + _addElementToDomAndClick(element); + } } /// Overrides some functions to allow testing From 7398fff62a2b3514e2430a07192c2cb795c548a9 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 4 Aug 2020 17:22:24 -0700 Subject: [PATCH 023/151] Functional getReadPath --- .../file_picker/example/lib/main.dart | 18 ++++++++++-------- .../file_picker/lib/file_picker.dart | 8 ++++---- .../lib/src/types/x_file/html.dart | 18 +++++++++++++++++- .../file_picker_web/lib/file_picker_web.dart | 12 ++++++++++-- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index 500a4b7c073c..10ff2e85f0ce 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -60,21 +60,23 @@ class _MyHomePageState extends State { } void _loadFile() async { - List file; + XPath path; if (_extensionController.text.isNotEmpty) { - List type = List(); - type.add(FileTypeFilterGroup(label: 'Example Files', fileExtensions: _extensionController.text.split(','))); - file = await loadFile(acceptedTypes: type); + List types = List(); + types.add(FileTypeFilterGroup(label: 'Example Files', fileExtensions: _extensionController.text.split(','))); + path = await getReadPath(acceptedTypes: types); } else { - file = await loadFile(); + path = await getReadPath(); } - String text = await file.first.readAsString(); + XFile file = XFile.fromXPath(path); + + String text = await file.readAsString(); _fileController.text = text; - if (file.first.name.isNotEmpty) { - _nameController.text = file.first.name; + if (file.name.isNotEmpty) { + _nameController.text = file.name; } } diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index 0ae8206f57db..c7644ae05a11 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -8,22 +8,22 @@ import 'dart:typed_data'; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; export 'package:file_picker_platform_interface/file_picker_platform_interface.dart' - show XFile, FileTypeFilterGroup; + show XFile, FileTypeFilterGroup, XPath; /// NEW API /// Open file dialog for loading files and return a file path -XPath getReadPath({List acceptedTypes}) { +Future getReadPath({List acceptedTypes}) { return FilePickerPlatform.instance.getReadPath(acceptedTypes: acceptedTypes); } /// Open file dialog for loading files and return a list of file paths -List getReadPaths({List acceptedTypes}) { +Future> getReadPaths({List acceptedTypes}) { return FilePickerPlatform.instance.getReadPaths(acceptedTypes: acceptedTypes); } /// Open file dialog for saving files and return a file path at which to save -XPath getSavePath() { +Future getSavePath() { throw UnimplementedError('loadFile() has not been implemented.'); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart index e5ecd11fee38..bfac1d3ffaff 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart @@ -5,11 +5,14 @@ import 'package:http/http.dart' as http show readBytes; import './base.dart'; +import '../types.dart'; + /// A XFile that works on web. /// /// It wraps the bytes of a selected file. class XFile extends XFileBase { - final String path; + final String path; // TODO: get rid of this guy + final XPath xPath; final Uint8List _initBytes; final int _length; @override @@ -22,15 +25,28 @@ class XFile extends XFileBase { /// /// `name` needs to be passed from the outside, since we only have /// access to it while we create the ObjectUrl. + // TODO: Replace this constructor XFile( this.path, { this.name, int length, Uint8List bytes, + this.xPath , }) : _initBytes = bytes, _length = length, super(path); + /// Constructor from XPath + XFile.fromXPath( + this.xPath, { + int length, + Uint8List bytes, + }) : _initBytes = bytes, + _length = length, + path = xPath.path, // TODO: just replace path everywhere + name = xPath.name, + super(xPath.path); + Future get _bytes async { if (_initBytes != null) { return Future.value(UnmodifiableUint8ListView(_initBytes)); diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 24048653bb13..7c9f949c16b2 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -105,7 +105,7 @@ class FilePickerPlugin extends FilePickerPlatform { /// Listen for file input element to change and retrieve files when /// this happens. Future> _getFilesWhenReady(InputElement element) { - final Completer> _completer = Completer(); + final Completer> _completer = Completer(); // Listens for element change element.onChange.first.then((event) { @@ -170,7 +170,15 @@ class FilePickerPlugin extends FilePickerPlatform { /// Open file dialog for loading files and return a file path @override Future getReadPath({List acceptedTypes}) { - return _readPathHelper(false, acceptedTypes); + Completer _completer = Completer(); + _readPathHelper(false, acceptedTypes).then((list) { + _completer.complete(list.first); + }) + .catchError((err) { + _completer.completeError(err); + }); + + return _completer.future; } /// Open file dialog for loading files and return a list of file paths From 4eccd883b9287d39f2f4f40b4e7109f2f2894995 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 4 Aug 2020 17:26:09 -0700 Subject: [PATCH 024/151] Do Nothing in getSavePath web --- packages/file_picker/file_picker_web/lib/file_picker_web.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 7c9f949c16b2..74e8e5595518 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -190,7 +190,7 @@ class FilePickerPlugin extends FilePickerPlatform { /// Open file dialog for saving files and return a file path at which to save @override Future getSavePath() { - throw UnimplementedError('loadFile() has not been implemented.'); + return Future.value(); } /// Web implementation of saveFile() From c9952594dacacde6fc8997bda1b00e2926ffa8f6 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 4 Aug 2020 17:39:38 -0700 Subject: [PATCH 025/151] Refactor XFile Class to use XPath --- .../lib/src/types/camera_device.dart | 18 ------- .../lib/src/types/image_source.dart | 12 ----- .../lib/src/types/lost_data_response.dart | 52 ------------------- .../lib/src/types/retrieve_type.dart | 12 ----- .../lib/src/types/types.dart | 4 -- .../lib/src/types/x_file/base.dart | 4 +- .../lib/src/types/x_file/html.dart | 13 +++-- .../lib/src/types/x_file/io.dart | 6 ++- .../lib/src/types/x_file/lost_data.dart | 49 ----------------- .../lib/src/types/x_file/unsupported.dart | 3 +- .../lib/src/types/x_file/x_file.dart | 1 - 11 files changed, 15 insertions(+), 159 deletions(-) delete mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/camera_device.dart delete mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/image_source.dart delete mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/lost_data_response.dart delete mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/retrieve_type.dart delete mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/lost_data.dart diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/camera_device.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/camera_device.dart deleted file mode 100644 index 6c70fd451a0e..000000000000 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/camera_device.dart +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/// Which camera to use when picking images/videos while source is `ImageSource.camera`. -/// -/// Not every device supports both of the positions. -enum CameraDevice { - /// Use the rear camera. - /// - /// In most of the cases, it is the default configuration. - rear, - - /// Use the front camera. - /// - /// Supported on all iPhones/iPads and some Android devices. - front, -} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/image_source.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/image_source.dart deleted file mode 100644 index 37981e3038f1..000000000000 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/image_source.dart +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/// Specifies the source where the picked image should come from. -enum ImageSource { - /// Opens up the device camera, letting the user to take a new picture. - camera, - - /// Opens the user's photo gallery. - gallery, -} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/lost_data_response.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/lost_data_response.dart deleted file mode 100644 index 73014654f6e6..000000000000 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/lost_data_response.dart +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:io'; - -import 'package:flutter/services.dart'; -import 'types.dart'; // TODO: use 'package:...' instead - -/// The response object of [ImagePicker.retrieveLostData]. -/// -/// Only applies to Android. -/// See also: -/// * [ImagePicker.retrieveLostData] for more details on retrieving lost data. -@Deprecated('Use methods that return a LostData object instead.') -class LostDataResponse { - /// Creates an instance with the given [file], [exception], and [type]. Any of - /// the params may be null, but this is never considered to be empty. - LostDataResponse({this.file, this.exception, this.type}); - - /// Initializes an instance with all member params set to null and considered - /// to be empty. - LostDataResponse.empty() - : file = null, - exception = null, - type = null, - _empty = true; - - /// Whether it is an empty response. - /// - /// An empty response should have [file], [exception] and [type] to be null. - bool get isEmpty => _empty; - - /// The file that was lost in a previous [pickImage] or [pickVideo] call due to MainActivity being destroyed. - /// - /// Can be null if [exception] exists. - final File file; - - /// The exception of the last [pickImage] or [pickVideo]. - /// - /// If the last [pickImage] or [pickVideo] threw some exception before the MainActivity destruction, this variable keeps that - /// exception. - /// You should handle this exception as if the [pickImage] or [pickVideo] got an exception when the MainActivity was not destroyed. - /// - /// Note that it is not the exception that caused the destruction of the MainActivity. - final PlatformException exception; - - /// Can either be [RetrieveType.image] or [RetrieveType.video]; - final RetrieveType type; - - bool _empty = false; -} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/retrieve_type.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/retrieve_type.dart deleted file mode 100644 index cc32be9711c2..000000000000 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/retrieve_type.dart +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/// The type of the retrieved data in a [LostDataResponse]. -enum RetrieveType { - /// A static picture. See [ImagePicker.pickImage]. - image, - - /// A video. See [ImagePicker.pickVideo]. - video -} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart index 1c7bd02345c5..b8adfaa2932e 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart @@ -1,7 +1,3 @@ -export 'camera_device.dart'; -export 'image_source.dart'; -export 'lost_data_response.dart'; -export 'retrieve_type.dart'; export 'x_file/x_file.dart'; export 'x_path/x_path.dart'; diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart index 4d12145ce14b..140d8f6edb12 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart @@ -1,6 +1,8 @@ import 'dart:convert'; import 'dart:typed_data'; +import '../types.dart'; + /// The interface for a XFile. /// /// A XFile is a container that wraps the path of a selected @@ -11,7 +13,7 @@ import 'dart:typed_data'; /// the methods should seem familiar. abstract class XFileBase { /// Construct a XFile - XFileBase(String path); + XFileBase(XPath path); /// Get the path of the picked file. /// diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart index bfac1d3ffaff..78014ff3dfcc 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart @@ -11,7 +11,6 @@ import '../types.dart'; /// /// It wraps the bytes of a selected file. class XFile extends XFileBase { - final String path; // TODO: get rid of this guy final XPath xPath; final Uint8List _initBytes; final int _length; @@ -25,16 +24,14 @@ class XFile extends XFileBase { /// /// `name` needs to be passed from the outside, since we only have /// access to it while we create the ObjectUrl. - // TODO: Replace this constructor XFile( - this.path, { + this.xPath, { this.name, int length, Uint8List bytes, - this.xPath , }) : _initBytes = bytes, _length = length, - super(path); + super(xPath); /// Constructor from XPath XFile.fromXPath( @@ -43,9 +40,8 @@ class XFile extends XFileBase { Uint8List bytes, }) : _initBytes = bytes, _length = length, - path = xPath.path, // TODO: just replace path everywhere name = xPath.name, - super(xPath.path); + super(xPath); Future get _bytes async { if (_initBytes != null) { @@ -74,4 +70,7 @@ class XFile extends XFileBase { final bytes = await _bytes; yield bytes.sublist(start ?? 0, end ?? bytes.length); } + + @override + String get path => xPath.path; } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart index 3ea492748bd1..14ae3e22a96a 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart @@ -4,13 +4,15 @@ import 'dart:typed_data'; import './base.dart'; +import '../types.dart'; + /// A XFile backed by a dart:io File. class XFile extends XFileBase { final File _file; /// Construct a XFile object backed by a dart:io File. - XFile(String path) - : _file = File(path), + XFile(XPath path) + : _file = File(path.path), super(path); @override diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/lost_data.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/lost_data.dart deleted file mode 100644 index 15bb236caece..000000000000 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/lost_data.dart +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/services.dart'; -import '../types.dart'; - -/// The response object of [ImagePicker.retrieveLostData]. -/// -/// Only applies to Android. -/// See also: -/// * [ImagePicker.retrieveLostData] for more details on retrieving lost data. -class LostData { - /// Creates an instance with the given [file], [exception], and [type]. Any of - /// the params may be null, but this is never considered to be empty. - LostData({this.file, this.exception, this.type}); - - /// Initializes an instance with all member params set to null and considered - /// to be empty. - LostData.empty() - : file = null, - exception = null, - type = null, - _empty = true; - - /// Whether it is an empty response. - /// - /// An empty response should have [file], [exception] and [type] to be null. - bool get isEmpty => _empty; - - /// The file that was lost in a previous [pickImage] or [pickVideo] call due to MainActivity being destroyed. - /// - /// Can be null if [exception] exists. - final XFile file; - - /// The exception of the last [pickImage] or [pickVideo]. - /// - /// If the last [pickImage] or [pickVideo] threw some exception before the MainActivity destruction, this variable keeps that - /// exception. - /// You should handle this exception as if the [pickImage] or [pickVideo] got an exception when the MainActivity was not destroyed. - /// - /// Note that it is not the exception that caused the destruction of the MainActivity. - final PlatformException exception; - - /// Can either be [RetrieveType.image] or [RetrieveType.video]; - final RetrieveType type; - - bool _empty = false; -} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart index cf384f5d939b..bab59d1da68d 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart @@ -1,6 +1,7 @@ import 'dart:typed_data'; import './base.dart'; +import '../types.dart'; /// A XFile is a cross-platform, simplified File abstraction. /// @@ -15,7 +16,7 @@ class XFile extends XFileBase { /// `path` of the file doesn't match what the user sees when selecting it /// (like in web) XFile( - String path, { + XPath path, { String name, int length, Uint8List bytes, diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart index b2a614ccb304..f966a7c9a3aa 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart @@ -1,4 +1,3 @@ -export 'lost_data.dart'; export 'unsupported.dart' if (dart.library.html) 'html.dart' if (dart.library.io) 'io.dart'; From dc400aae22eeb2e7a7743968ac4a047477ff7347 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 10 Aug 2020 15:32:28 -0700 Subject: [PATCH 026/151] Remove XPath, update API --- .../file_picker/example/lib/main.dart | 9 ++++-- .../file_picker/lib/file_picker.dart | 25 +++++---------- .../method_channel_file_picker.dart | 24 +++++++++----- .../file_picker_interface.dart | 23 +++----------- .../lib/src/types/types.dart | 7 ----- .../lib/src/types/x_file/base.dart | 6 ++-- .../lib/src/types/x_file/html.dart | 31 +++++-------------- .../lib/src/types/x_file/io.dart | 4 +-- .../lib/src/types/x_file/unsupported.dart | 3 +- .../lib/src/types/x_path/x_path.dart | 18 ----------- .../file_picker_web/lib/file_picker_web.dart | 12 +++---- 11 files changed, 53 insertions(+), 109 deletions(-) delete mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/x_path/x_path.dart diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index 10ff2e85f0ce..b9b3e30a1e8e 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -60,13 +60,13 @@ class _MyHomePageState extends State { } void _loadFile() async { - XPath path; + XFile file; if (_extensionController.text.isNotEmpty) { List types = List(); types.add(FileTypeFilterGroup(label: 'Example Files', fileExtensions: _extensionController.text.split(','))); - path = await getReadPath(acceptedTypes: types); + file = await loadFile(acceptedTypes: types); } else { - path = await getReadPath(); + file = await loadFile(); } XFile file = XFile.fromXPath(path); @@ -137,3 +137,6 @@ class _MyHomePageState extends State { ); } } + +class XPath { +} diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index c7644ae05a11..a181ed58c65a 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -13,29 +13,20 @@ export 'package:file_picker_platform_interface/file_picker_platform_interface.da /// NEW API /// Open file dialog for loading files and return a file path -Future getReadPath({List acceptedTypes}) { +Future loadFile({List acceptedTypes}) { return FilePickerPlatform.instance.getReadPath(acceptedTypes: acceptedTypes); } /// Open file dialog for loading files and return a list of file paths -Future> getReadPaths({List acceptedTypes}) { +Future> loadFiles({List acceptedTypes}) { return FilePickerPlatform.instance.getReadPaths(acceptedTypes: acceptedTypes); } -/// Open file dialog for saving files and return a file path at which to save -Future getSavePath() { - throw UnimplementedError('loadFile() has not been implemented.'); -} - - -/// OLD API - /// Saves File to user's file system -void saveFile(Uint8List data, {String type = '', String suggestedName}) async { - return FilePickerPlatform.instance.saveFile(data, type: type, suggestedName: suggestedName); -} - -/// Loads File from user's file system -Future> loadFile({List acceptedTypes}) { - return FilePickerPlatform.instance.loadFile(acceptedTypes: acceptedTypes); +Future getSavePath( + XFile file, { + String type = '', + String suggestedName, +}) async { + return FilePickerPlatform.instance.getSavePath(data, type: type, suggestedName: suggestedName); } \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart index 3e90f4dad471..6b8925ba746b 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart @@ -13,10 +13,10 @@ const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); /// An implementation of [FilePickerPlatform] that uses method channels. class MethodChannelFilePicker extends FilePickerPlatform { - /// Load file from user's computer and return it as an XFile + /// Load a file from user's computer and return it as an XFile @override - Future> loadFile({List acceptedTypes}) { - return _channel.invokeMethod>( + Future loadFile({List acceptedTypes}) { + return _channel.invokeMethod( 'loadFile', { 'acceptedTypes': acceptedTypes, @@ -24,14 +24,24 @@ class MethodChannelFilePicker extends FilePickerPlatform { ); } + /// Load multiple files from user's computer and return it as an XFile + @override + Future> loadFiles({List acceptedTypes}) { + return _channel.invokeMethod>( + 'loadFiles', + { + 'acceptedTypes': acceptedTypes, + }, + ); + } + /// Saves the file to user's Disk @override - void saveFile(Uint8List data, {String type, String suggestedName}) async { - await _channel.invokeMethod( + Future getSavePath() async { + return _channel.invokeMethod( 'saveFile', { - 'type': type, - 'suggestedName': suggestedName, + }, ); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index 2d9fdc1178fd..ef6f9ca82b17 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -41,30 +41,17 @@ abstract class FilePickerPlatform extends PlatformInterface { } /// Open file dialog for loading files and return a file path - Future getReadPath({List acceptedTypes}) { - throw UnimplementedError('getReadPath() has not been implemented.'); - } - - /// Open file dialog for loading files and return a list of file paths - Future> getReadPaths({List acceptedTypes}) { - throw UnimplementedError('getReadPaths() has not been implemented.'); - } - - /// Open file dialog for saving files and return a file path at which to save - Future getSavePath() { + Future loadFile({List acceptedTypes}) { throw UnimplementedError('loadFile() has not been implemented.'); } - - /// OLD API: - - /// Load file from user's computer and return it as an XFile - Future> loadFile({List acceptedTypes}) { + /// Open file dialog for loading files and return a list of file paths + Future> loadFiles({List acceptedTypes}) { throw UnimplementedError('loadFile() has not been implemented.'); } - /// Saves the file to user's Disk - void saveFile(Uint8List data, {String type, String suggestedName}) async { + /// Open file dialog for saving files and return a file path at which to save + Future getSavePath() { throw UnimplementedError('saveFile() has not been implemented.'); } } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart index b8adfaa2932e..05f40e1a6adf 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart @@ -1,10 +1,3 @@ export 'x_file/x_file.dart'; -export 'x_path/x_path.dart'; export 'filter_group/filter_group.dart'; - -/// Denotes that an image is being picked. -const String kTypeImage = 'image'; - -/// Denotes that a video is being picked. -const String kTypeVideo = 'video'; diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart index 140d8f6edb12..ba3e9a57cada 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart @@ -1,8 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; -import '../types.dart'; - /// The interface for a XFile. /// /// A XFile is a container that wraps the path of a selected @@ -13,7 +11,7 @@ import '../types.dart'; /// the methods should seem familiar. abstract class XFileBase { /// Construct a XFile - XFileBase(XPath path); + XFileBase(String path); /// Get the path of the picked file. /// @@ -66,4 +64,4 @@ abstract class XFileBase { Stream openRead([int start, int end]) { throw UnimplementedError('openRead() has not been implemented.'); } -} +} \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart index 78014ff3dfcc..6284fb7a5cf7 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart @@ -5,13 +5,11 @@ import 'package:http/http.dart' as http show readBytes; import './base.dart'; -import '../types.dart'; - /// A XFile that works on web. /// /// It wraps the bytes of a selected file. class XFile extends XFileBase { - final XPath xPath; + final String path; final Uint8List _initBytes; final int _length; @override @@ -25,23 +23,13 @@ class XFile extends XFileBase { /// `name` needs to be passed from the outside, since we only have /// access to it while we create the ObjectUrl. XFile( - this.xPath, { - this.name, - int length, - Uint8List bytes, - }) : _initBytes = bytes, - _length = length, - super(xPath); - - /// Constructor from XPath - XFile.fromXPath( - this.xPath, { - int length, - Uint8List bytes, - }) : _initBytes = bytes, + this.path, { + this.name, + int length, + Uint8List bytes, + }) : _initBytes = bytes, _length = length, - name = xPath.name, - super(xPath); + super(path); Future get _bytes async { if (_initBytes != null) { @@ -70,7 +58,4 @@ class XFile extends XFileBase { final bytes = await _bytes; yield bytes.sublist(start ?? 0, end ?? bytes.length); } - - @override - String get path => xPath.path; -} +} \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart index 14ae3e22a96a..acc3986975d3 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart @@ -11,8 +11,8 @@ class XFile extends XFileBase { final File _file; /// Construct a XFile object backed by a dart:io File. - XFile(XPath path) - : _file = File(path.path), + XFile(String path) + : _file = File(path), super(path); @override diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart index bab59d1da68d..cf384f5d939b 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart @@ -1,7 +1,6 @@ import 'dart:typed_data'; import './base.dart'; -import '../types.dart'; /// A XFile is a cross-platform, simplified File abstraction. /// @@ -16,7 +15,7 @@ class XFile extends XFileBase { /// `path` of the file doesn't match what the user sees when selecting it /// (like in web) XFile( - XPath path, { + String path, { String name, int length, Uint8List bytes, diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_path/x_path.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_path/x_path.dart deleted file mode 100644 index 07df81e9dce8..000000000000 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_path/x_path.dart +++ /dev/null @@ -1,18 +0,0 @@ -/// Cross platform path class -class XPath { - /// XPath constructor - XPath(this._path, {String name, int modified, int created}): - _modified = modified, - _created = created, - _name = name; - - final String _path; - final String _name; - final int _modified; - final int _created; - - String get path => _path; - int get modified => _modified; - int get created => _created; - String get name => _name; -} \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 74e8e5595518..18695225fbc7 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -169,7 +169,7 @@ class FilePickerPlugin extends FilePickerPlatform { /// Open file dialog for loading files and return a file path @override - Future getReadPath({List acceptedTypes}) { + Future loadFile({List acceptedTypes}) { Completer _completer = Completer(); _readPathHelper(false, acceptedTypes).then((list) { _completer.complete(list.first); @@ -183,18 +183,14 @@ class FilePickerPlugin extends FilePickerPlatform { /// Open file dialog for loading files and return a list of file paths @override - Future> getReadPaths({List acceptedTypes}) { + Future> loadFiles({List acceptedTypes}) { return _readPathHelper(true, acceptedTypes); } - - /// Open file dialog for saving files and return a file path at which to save + @override - Future getSavePath() { - return Future.value(); - } + Future getSavePath() => Future.value(); /// Web implementation of saveFile() - @override void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { // Create blob from data final blob = createBlob(data, type); From 299912988fc225c5f0621390873b95c6004a8c99 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 10 Aug 2020 17:04:54 -0700 Subject: [PATCH 027/151] Move Save Logic to XFile and Remove XPath new fromData constructor in XFile class --- .../file_picker/example/lib/main.dart | 9 +-- .../file_picker/lib/file_picker.dart | 12 ++-- .../lib/src/types/x_file/base.dart | 5 ++ .../lib/src/types/x_file/html.dart | 72 +++++++++++++++++-- .../lib/src/types/x_file/io.dart | 11 +++ .../lib/src/types/x_file/unsupported.dart | 11 +++ .../file_picker_web/lib/file_picker_web.dart | 60 +++++----------- 7 files changed, 119 insertions(+), 61 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index b9b3e30a1e8e..2790544fada8 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -51,12 +51,15 @@ class _MyHomePageState extends State { Uint8List data; data = Uint8List.fromList(_fileController.text.codeUnits); + XFile new_file; // await? if (_nameController.text == '') { - saveFile(data, type: 'text/plain'); + new_file = XFile.fromData(data); } else { - saveFile(data, type: 'text/plain', suggestedName: _nameController.text); + new_file = XFile.fromData(data, name: _nameController.text); } + + new_file.saveTo(''); } void _loadFile() async { @@ -69,8 +72,6 @@ class _MyHomePageState extends State { file = await loadFile(); } - XFile file = XFile.fromXPath(path); - String text = await file.readAsString(); _fileController.text = text; diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index a181ed58c65a..a1e5743f7382 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -14,19 +14,15 @@ export 'package:file_picker_platform_interface/file_picker_platform_interface.da /// Open file dialog for loading files and return a file path Future loadFile({List acceptedTypes}) { - return FilePickerPlatform.instance.getReadPath(acceptedTypes: acceptedTypes); + return FilePickerPlatform.instance.loadFile(acceptedTypes: acceptedTypes); } /// Open file dialog for loading files and return a list of file paths Future> loadFiles({List acceptedTypes}) { - return FilePickerPlatform.instance.getReadPaths(acceptedTypes: acceptedTypes); + return FilePickerPlatform.instance.loadFiles(acceptedTypes: acceptedTypes); } /// Saves File to user's file system -Future getSavePath( - XFile file, { - String type = '', - String suggestedName, -}) async { - return FilePickerPlatform.instance.getSavePath(data, type: type, suggestedName: suggestedName); +Future getSavePath() async { + return FilePickerPlatform.instance.getSavePath(); } \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart index ba3e9a57cada..4ea55b864cb7 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart @@ -13,6 +13,11 @@ abstract class XFileBase { /// Construct a XFile XFileBase(String path); + /// Save the XFile at the indicated file path. + void saveTo(String path) { + throw UnimplementedError('saveTo has not been implemented.'); + } + /// Get the path of the picked file. /// /// This should only be used as a backwards-compatibility clutch diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart index 6284fb7a5cf7..6f0e3f39dfce 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart @@ -2,6 +2,8 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:http/http.dart' as http show readBytes; +import 'package:meta/meta.dart'; +import 'dart:html'; import './base.dart'; @@ -9,11 +11,12 @@ import './base.dart'; /// /// It wraps the bytes of a selected file. class XFile extends XFileBase { - final String path; - final Uint8List _initBytes; + String path; + final Uint8List _data; final int _length; @override final String name; + Element _target; /// Construct a XFile object from its ObjectUrl. /// @@ -27,13 +30,32 @@ class XFile extends XFileBase { this.name, int length, Uint8List bytes, - }) : _initBytes = bytes, + }) : _data = bytes, _length = length, - super(path); + super(path) { + // Create a DOM container where we can host the anchor. + _target = _ensureInitialized(this.name + '-x-file-dom-element'); + } + + /// Construct an XFile from its data + XFile.fromData( + Uint8List bytes, { + this.name, + int length, + }) : _data = bytes, + _length = length, + super('') { + Blob blob = Blob([bytes]); + this.path = Url.createObjectUrl(blob); + // Create a DOM container where we can host the anchor. + _target = _ensureInitialized(this.name + '-x-file-dom-element'); + } + + Future get _bytes async { - if (_initBytes != null) { - return Future.value(UnmodifiableUint8ListView(_initBytes)); + if (_data != null) { + return Future.value(UnmodifiableUint8ListView(_data)); } return http.readBytes(path); } @@ -58,4 +80,42 @@ class XFile extends XFileBase { final bytes = await _bytes; yield bytes.sublist(start ?? 0, end ?? bytes.length); } + + /// Saves the data of this XFile at the location indicated by path. + /// For the web implementation, the path variable is ignored. + void saveTo(String path) { + // Create an tag with the appropriate download attributes and click it + final AnchorElement element = createAnchorElement(this.path, this.name); + + _addElementToDomAndClick(element); + } + + /// Create anchor element with download attribute + @visibleForTesting + AnchorElement createAnchorElement(String href, String suggestedName) { + final element = AnchorElement(href: href); + element.download = suggestedName; + return element; + } + + void _addElementToDomAndClick(Element element) { + // Add the file input element and click it + // All previous elements will be removed before adding the new one + _target.children.clear(); + _target.children.add(element); + element.click(); + } + + /// Initializes a DOM container where we can host elements. + Element _ensureInitialized(String id) { + var target = querySelector('#${id}'); + if (target == null) { + final Element targetElement = + Element.tag('flt-x-file-input')..id = id; + + querySelector('body').children.add(targetElement); + target = targetElement; + } + return target; + } } \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart index acc3986975d3..9966ab39bbb4 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart @@ -9,10 +9,21 @@ import '../types.dart'; /// A XFile backed by a dart:io File. class XFile extends XFileBase { final File _file; + final Uint8List _data; /// Construct a XFile object backed by a dart:io File. XFile(String path) : _file = File(path), + _data = null, + super(path); + + /// Construct an XFile from its data + XFile.fromData(Uint8List data, { + String path, + String name, + int length, + }): _data = data, + _file = File(path), super(path); @override diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart index cf384f5d939b..8acdeb329b54 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart @@ -23,4 +23,15 @@ class XFile extends XFileBase { throw UnimplementedError( 'XFile is not available in your current platform.'); } + + /// Construct a XFile object from its data + XFile.fromData( + Uint8List data, { + String path, + String name, + int length, + }) : super(path) { + throw UnimplementedError( + 'XFile is not available in your current platform.'); + } } diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 18695225fbc7..242af28b66ad 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -63,8 +63,8 @@ class FilePickerPlugin extends FilePickerPlatform { element.click(); } - List _getXPathsFromFiles (List files) { - List xPaths = List(); + List _getXFilesFromFiles (List files) { + List xFiles = List(); for (File file in files) { String url = Url.createObjectUrl(file); @@ -72,10 +72,10 @@ class FilePickerPlugin extends FilePickerPlatform { int length = file.size; int modified = file.lastModified; - xPaths.add(XPath(url, name: name, modified: modified)); + xFiles.add(XFile(url, name: name)); } - return xPaths; + return xFiles; } /// Getter for retrieving files from an input element @@ -88,8 +88,8 @@ class FilePickerPlugin extends FilePickerPlatform { return element?.files ?? []; } - Future _getFileWhenReady(InputElement element) { - final Completer _completer = Completer(); + Future _getFileWhenReady(InputElement element) { + final Completer _completer = Completer(); _getFilesWhenReady(element) .then((list) { @@ -104,16 +104,16 @@ class FilePickerPlugin extends FilePickerPlatform { /// Listen for file input element to change and retrieve files when /// this happens. - Future> _getFilesWhenReady(InputElement element) { - final Completer> _completer = Completer(); + Future> _getFilesWhenReady(InputElement element) { + final Completer> _completer = Completer(); // Listens for element change element.onChange.first.then((event) { // File type from dart:html class final List files = getFilesFromInputElement(element); - // Create XPath from dart:html Files - final returnPaths = _getXPathsFromFiles(files); + // Create XFile from dart:html Files + final returnPaths = _getXFilesFromFiles(files); _completer.complete(returnPaths); }); @@ -127,20 +127,6 @@ class FilePickerPlugin extends FilePickerPlatform { return _completer.future; } - /// Create anchor element with download attribute - @visibleForTesting - AnchorElement createAnchorElement(String href, String suggestedName) { - final element = AnchorElement(href: href); - element.download = suggestedName; - return element; - } - - /// Create blob with specified data of indicated type - @visibleForTesting - Blob createBlob(Uint8List data, String type) { - return type.isEmpty ? Blob([data]) : Blob([data], type); - } - /// Initializes a DOM container where we can host elements. Element _ensureInitialized(String id) { var target = querySelector('#${id}'); @@ -156,8 +142,8 @@ class FilePickerPlugin extends FilePickerPlatform { /// NEW API - // Load Helper - Future> _readPathHelper (bool multiple, List acceptedTypes) { + /// Load Helper + Future> _loadFileHelper (bool multiple, List acceptedTypes) { final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); final FileUploadInputElement element = createFileInputElement(acceptedTypeString, multiple); @@ -169,9 +155,9 @@ class FilePickerPlugin extends FilePickerPlatform { /// Open file dialog for loading files and return a file path @override - Future loadFile({List acceptedTypes}) { - Completer _completer = Completer(); - _readPathHelper(false, acceptedTypes).then((list) { + Future loadFile({List acceptedTypes}) { + Completer _completer = Completer(); + _loadFileHelper(false, acceptedTypes).then((list) { _completer.complete(list.first); }) .catchError((err) { @@ -183,24 +169,12 @@ class FilePickerPlugin extends FilePickerPlatform { /// Open file dialog for loading files and return a list of file paths @override - Future> loadFiles({List acceptedTypes}) { - return _readPathHelper(true, acceptedTypes); + Future> loadFiles({List acceptedTypes}) { + return _loadFileHelper(true, acceptedTypes); } @override Future getSavePath() => Future.value(); - - /// Web implementation of saveFile() - void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { - // Create blob from data - final blob = createBlob(data, type); - String url = Url.createObjectUrl(blob); - - // Create an tag with the appropriate download attributes and click it - final AnchorElement element = createAnchorElement(url, suggestedName); - - _addElementToDomAndClick(element); - } } /// Overrides some functions to allow testing From 1abe10678839f1419a615ec44e7696397bafbc9d Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 17 Aug 2020 15:34:58 -0700 Subject: [PATCH 028/151] Add XType and XTypeGroup --- .../file_picker/example/lib/main.dart | 10 ++-- .../file_picker/lib/file_picker.dart | 10 ++-- .../method_channel_file_picker.dart | 8 ++-- .../file_picker_interface.dart | 4 +- .../src/types/filter_group/filter_group.dart | 47 +++++++++++++++++-- .../lib/src/types/x_file/x_file.dart | 2 +- .../pubspec.yaml | 1 + .../file_picker_web/lib/file_picker_web.dart | 18 +++---- 8 files changed, 73 insertions(+), 27 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index 2790544fada8..b7a10fcc8da6 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -65,9 +65,13 @@ class _MyHomePageState extends State { void _loadFile() async { XFile file; if (_extensionController.text.isNotEmpty) { - List types = List(); - types.add(FileTypeFilterGroup(label: 'Example Files', fileExtensions: _extensionController.text.split(','))); - file = await loadFile(acceptedTypes: types); + List typeGroups = List(); + + List types = List(); + _extensionController.text.split(',').forEach((type) => types.add(XType.fromExtension(type))); + + typeGroups.add(XTypeGroup(label: 'Example Files', fileTypes: types)); + file = await loadFile(acceptedTypeGroups: typeGroups); } else { file = await loadFile(); } diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index a1e5743f7382..0857ecc11bb6 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -8,18 +8,18 @@ import 'dart:typed_data'; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; export 'package:file_picker_platform_interface/file_picker_platform_interface.dart' - show XFile, FileTypeFilterGroup, XPath; + show XFile, XTypeGroup, XType; /// NEW API /// Open file dialog for loading files and return a file path -Future loadFile({List acceptedTypes}) { - return FilePickerPlatform.instance.loadFile(acceptedTypes: acceptedTypes); +Future loadFile({List acceptedTypeGroups}) { + return FilePickerPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups); } /// Open file dialog for loading files and return a list of file paths -Future> loadFiles({List acceptedTypes}) { - return FilePickerPlatform.instance.loadFiles(acceptedTypes: acceptedTypes); +Future> loadFiles({List acceptedTypeGroups}) { + return FilePickerPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups); } /// Saves File to user's file system diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart index 6b8925ba746b..5cdcf5d86fdb 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart @@ -15,22 +15,22 @@ const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); class MethodChannelFilePicker extends FilePickerPlatform { /// Load a file from user's computer and return it as an XFile @override - Future loadFile({List acceptedTypes}) { + Future loadFile({List acceptedTypeGroups}) { return _channel.invokeMethod( 'loadFile', { - 'acceptedTypes': acceptedTypes, + 'acceptedTypes': acceptedTypeGroups, }, ); } /// Load multiple files from user's computer and return it as an XFile @override - Future> loadFiles({List acceptedTypes}) { + Future> loadFiles({List acceptedTypeGroups}) { return _channel.invokeMethod>( 'loadFiles', { - 'acceptedTypes': acceptedTypes, + 'acceptedTypes': acceptedTypeGroups, }, ); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index ef6f9ca82b17..260def18f868 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -41,12 +41,12 @@ abstract class FilePickerPlatform extends PlatformInterface { } /// Open file dialog for loading files and return a file path - Future loadFile({List acceptedTypes}) { + Future loadFile({List acceptedTypeGroups}) { throw UnimplementedError('loadFile() has not been implemented.'); } /// Open file dialog for loading files and return a list of file paths - Future> loadFiles({List acceptedTypes}) { + Future> loadFiles({List acceptedTypeGroups}) { throw UnimplementedError('loadFile() has not been implemented.'); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart index 186e1eca42b3..f79c0c679e32 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart @@ -12,10 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// A set of allowed file types. -class FileTypeFilterGroup { +// TODO: should we be using this package or just extracting the important conversion data from it? +import 'package:mime_type/mime_type.dart'; + +/// A set of allowed XTypes +class XTypeGroup { /// Creates a new group with the given label and file extensions. - const FileTypeFilterGroup({this.label, this.fileExtensions}); + const XTypeGroup({this.label, this.fileTypes}); /// The label for the grouping. On platforms that support selectable groups, /// this will be visible to the user for selecting the group. @@ -24,5 +27,41 @@ class FileTypeFilterGroup { /// A list of allowed file extensions. E.g., ['png', 'jpg', 'jpeg', 'gif']. /// /// A null or empty list indicates any type is allowed. - final List fileExtensions; + final List fileTypes; } + +/// A cross platform file type +class XType { + /// Variables to store type + String _mime; + String _extension; + + /// Default constructor + XType({ + String extension, + String mime, + }) : this._extension = extension, + this._mime = mime; + + /// Constructors that take other files types as input + XType.fromMime(String mime) : this._mime = mime; + + /// Constructor that takes extension as input + XType.fromExtension(String extension) : this._extension = extension; + + /// Get the mime type from this XType + String get mime { + if (mime == null) { + _mime = mimeFromExtension(_extension); + } + return _mime; + } + + /// Get the extension from this XType + String get extension { + if (_extension == null) { + _extension = extensionFromMime(_mime); + } + return _extension; + } +} \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart index f966a7c9a3aa..72d156cc58fd 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart @@ -1,3 +1,3 @@ export 'unsupported.dart' if (dart.library.html) 'html.dart' - if (dart.library.io) 'io.dart'; + if (dart.library.io) 'io.dart'; \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/pubspec.yaml b/packages/file_picker/file_picker_platform_interface/pubspec.yaml index 249d9f1f9a89..9b8de97e38a6 100644 --- a/packages/file_picker/file_picker_platform_interface/pubspec.yaml +++ b/packages/file_picker/file_picker_platform_interface/pubspec.yaml @@ -11,6 +11,7 @@ dependencies: meta: ^1.0.5 http: ^0.12.0+1 plugin_platform_interface: ^1.0.1 + mime_type: ^0.3.1 dev_dependencies: test: ^1.15.0 diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 242af28b66ad..f99d44c3717d 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -31,10 +31,12 @@ class FilePickerPlugin extends FilePickerPlatform { } /// Convert list of filter groups to a comma-separated string - String _getStringFromFilterGroup (List acceptedTypes) { + String _getStringFromFilterGroup (List acceptedTypes) { List allExtensions = List(); - for (FileTypeFilterGroup group in acceptedTypes ?? []) { - allExtensions += group.fileExtensions; + for (XTypeGroup group in acceptedTypes ?? []) { + for (XType type in group.fileTypes ?? []) { + allExtensions.add(type.extension); + } } return allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; } @@ -143,7 +145,7 @@ class FilePickerPlugin extends FilePickerPlatform { /// NEW API /// Load Helper - Future> _loadFileHelper (bool multiple, List acceptedTypes) { + Future> _loadFileHelper (bool multiple, List acceptedTypes) { final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); final FileUploadInputElement element = createFileInputElement(acceptedTypeString, multiple); @@ -155,9 +157,9 @@ class FilePickerPlugin extends FilePickerPlatform { /// Open file dialog for loading files and return a file path @override - Future loadFile({List acceptedTypes}) { + Future loadFile({List acceptedTypeGroups}) { Completer _completer = Completer(); - _loadFileHelper(false, acceptedTypes).then((list) { + _loadFileHelper(false, acceptedTypeGroups).then((list) { _completer.complete(list.first); }) .catchError((err) { @@ -169,8 +171,8 @@ class FilePickerPlugin extends FilePickerPlatform { /// Open file dialog for loading files and return a list of file paths @override - Future> loadFiles({List acceptedTypes}) { - return _loadFileHelper(true, acceptedTypes); + Future> loadFiles({List acceptedTypeGroups}) { + return _loadFileHelper(true, acceptedTypeGroups); } @override From 52a17fe9a6a7705e208b7324c5f6506405e41eff Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 17 Aug 2020 16:57:33 -0700 Subject: [PATCH 029/151] Remove Named Constructors --- packages/file_picker/file_picker/example/lib/main.dart | 2 +- .../lib/src/types/filter_group/filter_group.dart | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index b7a10fcc8da6..e98e5d99fbc9 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -68,7 +68,7 @@ class _MyHomePageState extends State { List typeGroups = List(); List types = List(); - _extensionController.text.split(',').forEach((type) => types.add(XType.fromExtension(type))); + _extensionController.text.split(',').forEach((type) => types.add(XType(extension: type))); typeGroups.add(XTypeGroup(label: 'Example Files', fileTypes: types)); file = await loadFile(acceptedTypeGroups: typeGroups); diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart index f79c0c679e32..76d0343d2c87 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart @@ -43,12 +43,6 @@ class XType { }) : this._extension = extension, this._mime = mime; - /// Constructors that take other files types as input - XType.fromMime(String mime) : this._mime = mime; - - /// Constructor that takes extension as input - XType.fromExtension(String extension) : this._extension = extension; - /// Get the mime type from this XType String get mime { if (mime == null) { From ee291fc3ebcc8d5c001383cd53148a750da320ac Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 17 Aug 2020 17:14:50 -0700 Subject: [PATCH 030/151] Bug Fix, 'accept' attribute requires extensions start with '.' --- .../file_picker/file_picker_web/lib/file_picker_web.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index f99d44c3717d..a9f58bbf0f54 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -35,7 +35,10 @@ class FilePickerPlugin extends FilePickerPlatform { List allExtensions = List(); for (XTypeGroup group in acceptedTypes ?? []) { for (XType type in group.fileTypes ?? []) { - allExtensions.add(type.extension); + if (type.extension == null) { + continue; + } + allExtensions.add('.' + type.extension); } } return allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; From e646680e5b8f5a48af5d89b16d0d7268aff8b560 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 18 Aug 2020 16:34:34 -0700 Subject: [PATCH 031/151] Rename Filter Group to XType --- .../file_picker_platform_interface/lib/src/types/types.dart | 2 +- .../{filter_group/filter_group.dart => x_type/x_type.dart} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename packages/file_picker/file_picker_platform_interface/lib/src/types/{filter_group/filter_group.dart => x_type/x_type.dart} (100%) diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart index 05f40e1a6adf..cae9d41efedf 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart @@ -1,3 +1,3 @@ export 'x_file/x_file.dart'; -export 'filter_group/filter_group.dart'; +export 'x_type/x_type.dart'; diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_type/x_type.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart rename to packages/file_picker/file_picker_platform_interface/lib/src/types/x_type/x_type.dart From a169c8124342974a8be8b97959a428f4581891b5 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 18 Aug 2020 17:30:38 -0700 Subject: [PATCH 032/151] Add XType to XFile and Update XFile small bug fix in XType mime to _mime (infinite loop) --- .../file_picker/example/lib/main.dart | 7 ++-- .../lib/src/types/x_file/base.dart | 3 +- .../lib/src/types/x_file/html.dart | 16 ++++++-- .../lib/src/types/x_file/io.dart | 38 +++++++++++++++++-- .../lib/src/types/x_type/x_type.dart | 2 +- 5 files changed, 54 insertions(+), 12 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index e98e5d99fbc9..78245d400fe0 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -52,11 +52,12 @@ class _MyHomePageState extends State { data = Uint8List.fromList(_fileController.text.codeUnits); XFile new_file; - // await? + XType type = XType(extension: '.txt'); + if (_nameController.text == '') { - new_file = XFile.fromData(data); + new_file = XFile.fromData(data, type: type); } else { - new_file = XFile.fromData(data, name: _nameController.text); + new_file = XFile.fromData(data, type: type, name: _nameController.text); } new_file.saveTo(''); diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart index 4ea55b864cb7..1b5c81f9bde0 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; +import '../x_type/x_type.dart'; /// The interface for a XFile. /// @@ -14,7 +15,7 @@ abstract class XFileBase { XFileBase(String path); /// Save the XFile at the indicated file path. - void saveTo(String path) { + void saveTo(String path) async { throw UnimplementedError('saveTo has not been implemented.'); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart index 6f0e3f39dfce..c1cc415c739f 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart @@ -7,14 +7,17 @@ import 'dart:html'; import './base.dart'; +import '../x_type/x_type.dart'; + /// A XFile that works on web. /// /// It wraps the bytes of a selected file. class XFile extends XFileBase { String path; + final XType type; + final Uint8List _data; final int _length; - @override final String name; Element _target; @@ -27,6 +30,7 @@ class XFile extends XFileBase { /// access to it while we create the ObjectUrl. XFile( this.path, { + this.type, this.name, int length, Uint8List bytes, @@ -40,12 +44,18 @@ class XFile extends XFileBase { /// Construct an XFile from its data XFile.fromData( Uint8List bytes, { + this.type, this.name, int length, }) : _data = bytes, _length = length, super('') { - Blob blob = Blob([bytes]); + Blob blob; + if (type.mime == null) { + blob = Blob([bytes]); + } else { + blob = Blob([bytes], type.mime); + } this.path = Url.createObjectUrl(blob); // Create a DOM container where we can host the anchor. _target = _ensureInitialized(this.name + '-x-file-dom-element'); @@ -83,7 +93,7 @@ class XFile extends XFileBase { /// Saves the data of this XFile at the location indicated by path. /// For the web implementation, the path variable is ignored. - void saveTo(String path) { + void saveTo(String path) async { // Create an tag with the appropriate download attributes and click it final AnchorElement element = createAnchorElement(this.path, this.name); diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart index 9966ab39bbb4..3538e6364804 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart @@ -9,22 +9,39 @@ import '../types.dart'; /// A XFile backed by a dart:io File. class XFile extends XFileBase { final File _file; + int _length; + final Uint8List _data; + final XType type; + /// Construct a XFile object backed by a dart:io File. - XFile(String path) + XFile(String path, { this.type }) : _file = File(path), _data = null, super(path); /// Construct an XFile from its data XFile.fromData(Uint8List data, { + this.type, String path, String name, int length, }): _data = data, _file = File(path), - super(path); + _length = length, + super(path) { + if (length == null) { + _length = data.length; + } + } + + @override + void saveTo(String path) async { + File fileToSave = File(path); + await fileToSave.writeAsBytes(_data); + await fileToSave.create(); + } @override String get path { @@ -38,22 +55,35 @@ class XFile extends XFileBase { @override Future length() { + if (_length != null) { + return Future.value(_length); + } return _file.length(); } @override Future readAsString({Encoding encoding = utf8}) { + if (_data != null) { + return Future.value(String.fromCharCodes(_data)); + } return _file.readAsString(encoding: encoding); } @override Future readAsBytes() { + if (_data != null) { + return Future.value(_data); + } return _file.readAsBytes(); } @override - Stream openRead([int start, int end]) { - return _file + Stream openRead([int start, int end]) async* { + if (_data != null) { + final bytes = _data; + yield bytes.sublist(start ?? 0, end ?? bytes.length); + } + yield* _file .openRead(start ?? 0, end) .map((chunk) => Uint8List.fromList(chunk)); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_type/x_type.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_type/x_type.dart index 76d0343d2c87..ace2f602d45b 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_type/x_type.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_type/x_type.dart @@ -45,7 +45,7 @@ class XType { /// Get the mime type from this XType String get mime { - if (mime == null) { + if (_mime == null) { _mime = mimeFromExtension(_extension); } return _mime; From b22754c3156f3df6b46b410eb818be91be6a0dad Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 18 Aug 2020 17:48:51 -0700 Subject: [PATCH 033/151] Refactor file_picker to file_selector --- .../lib/file_picker_platform_interface.dart | 2 -- .../file_selector}/CHANGELOG.md | 0 .../file_selector}/LICENSE | 0 .../file_selector}/README.md | 0 .../file_selector}/example/.gitignore | 0 .../file_selector}/example/.metadata | 0 .../file_selector}/example/README.md | 0 .../file_selector}/example/lib/main.dart | 2 +- .../file_selector}/example/pubspec.yaml | 2 +- .../example/test/widget_test.dart | 0 .../file_selector}/example/web/favicon.png | Bin .../example/web/icons/Icon-192.png | Bin .../example/web/icons/Icon-512.png | Bin .../file_selector}/example/web/index.html | 0 .../file_selector}/example/web/manifest.json | 0 .../file_selector/lib/file_selector.dart} | 10 ++++---- .../file_selector}/pubspec.yaml | 12 ++++----- .../file_selector}/test/file_picker_test.dart | 0 .../CHANGELOG.md | 0 .../file_selector_platform_interface}/LICENSE | 0 .../README.md | 0 .../lib/file_selector_platform_interface.dart | 2 ++ .../method_channel_file_selector.dart} | 8 +++--- .../file_selector_interface.dart} | 24 +++++++++--------- .../lib/src/types/types.dart | 0 .../lib/src/types/x_file/base.dart | 0 .../lib/src/types/x_file/html.dart | 0 .../lib/src/types/x_file/io.dart | 0 .../lib/src/types/x_file/unsupported.dart | 0 .../lib/src/types/x_file/x_file.dart | 0 .../lib/src/types/x_type/x_type.dart | 0 .../pubspec.yaml | 2 +- .../file_picker_platform_interface_test.dart | 4 +-- .../file_selector_web}/CHANGELOG.md | 0 .../file_selector_web}/LICENSE | 0 .../file_selector_web}/README.md | 0 .../lib/file_selector_web.dart} | 24 +++++++++--------- .../file_selector_web}/pubspec.yaml | 14 +++++----- .../file_selector_web}/test/README.md | 0 .../file_selector_web}/test/lib/main.dart | 0 .../file_selector_web}/test/pubspec.yaml | 0 .../test/test_driver/web_e2e.dart | 2 +- .../test/test_driver/web_e2e_test.dart | 0 .../file_selector_web}/test/web/index.html | 0 44 files changed, 54 insertions(+), 54 deletions(-) delete mode 100644 packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart rename packages/{file_picker/file_picker => file_selector/file_selector}/CHANGELOG.md (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/LICENSE (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/README.md (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/.gitignore (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/.metadata (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/README.md (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/lib/main.dart (98%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/pubspec.yaml (99%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/test/widget_test.dart (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/web/favicon.png (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/web/icons/Icon-192.png (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/web/icons/Icon-512.png (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/web/index.html (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/web/manifest.json (100%) rename packages/{file_picker/file_picker/lib/file_picker.dart => file_selector/file_selector/lib/file_selector.dart} (59%) rename packages/{file_picker/file_picker => file_selector/file_selector}/pubspec.yaml (72%) rename packages/{file_picker/file_picker => file_selector/file_selector}/test/file_picker_test.dart (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/CHANGELOG.md (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/LICENSE (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/README.md (100%) create mode 100644 packages/file_selector/file_selector_platform_interface/lib/file_selector_platform_interface.dart rename packages/{file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart => file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart} (79%) rename packages/{file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart => file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart} (68%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/lib/src/types/types.dart (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/lib/src/types/x_file/base.dart (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/lib/src/types/x_file/html.dart (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/lib/src/types/x_file/io.dart (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/lib/src/types/x_file/unsupported.dart (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/lib/src/types/x_file/x_file.dart (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/lib/src/types/x_type/x_type.dart (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/pubspec.yaml (94%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/test/file_picker_platform_interface_test.dart (91%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/CHANGELOG.md (100%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/LICENSE (100%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/README.md (100%) rename packages/{file_picker/file_picker_web/lib/file_picker_web.dart => file_selector/file_selector_web/lib/file_selector_web.dart} (87%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/pubspec.yaml (68%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/test/README.md (100%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/test/lib/main.dart (100%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/test/pubspec.yaml (100%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/test/test_driver/web_e2e.dart (98%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/test/test_driver/web_e2e_test.dart (100%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/test/web/index.html (100%) diff --git a/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart deleted file mode 100644 index f851916ca9e4..000000000000 --- a/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'src/platform_interface/file_picker_interface.dart'; -export 'src/types/types.dart'; diff --git a/packages/file_picker/file_picker/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md similarity index 100% rename from packages/file_picker/file_picker/CHANGELOG.md rename to packages/file_selector/file_selector/CHANGELOG.md diff --git a/packages/file_picker/file_picker/LICENSE b/packages/file_selector/file_selector/LICENSE similarity index 100% rename from packages/file_picker/file_picker/LICENSE rename to packages/file_selector/file_selector/LICENSE diff --git a/packages/file_picker/file_picker/README.md b/packages/file_selector/file_selector/README.md similarity index 100% rename from packages/file_picker/file_picker/README.md rename to packages/file_selector/file_selector/README.md diff --git a/packages/file_picker/file_picker/example/.gitignore b/packages/file_selector/file_selector/example/.gitignore similarity index 100% rename from packages/file_picker/file_picker/example/.gitignore rename to packages/file_selector/file_selector/example/.gitignore diff --git a/packages/file_picker/file_picker/example/.metadata b/packages/file_selector/file_selector/example/.metadata similarity index 100% rename from packages/file_picker/file_picker/example/.metadata rename to packages/file_selector/file_selector/example/.metadata diff --git a/packages/file_picker/file_picker/example/README.md b/packages/file_selector/file_selector/example/README.md similarity index 100% rename from packages/file_picker/file_picker/example/README.md rename to packages/file_selector/file_selector/example/README.md diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart similarity index 98% rename from packages/file_picker/file_picker/example/lib/main.dart rename to packages/file_selector/file_selector/example/lib/main.dart index 78245d400fe0..2ae04cfb2b4e 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -1,7 +1,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; -import 'package:file_picker/file_picker.dart'; +import 'package:file_selector/file_selector.dart'; void main() { runApp(MyApp()); diff --git a/packages/file_picker/file_picker/example/pubspec.yaml b/packages/file_selector/file_selector/example/pubspec.yaml similarity index 99% rename from packages/file_picker/file_picker/example/pubspec.yaml rename to packages/file_selector/file_selector/example/pubspec.yaml index c292d1b300aa..58f0abbf2658 100644 --- a/packages/file_picker/file_picker/example/pubspec.yaml +++ b/packages/file_selector/file_selector/example/pubspec.yaml @@ -24,7 +24,7 @@ dependencies: flutter: sdk: flutter - file_picker: + file_selector: path: ../ # The following adds the Cupertino Icons font to your application. diff --git a/packages/file_picker/file_picker/example/test/widget_test.dart b/packages/file_selector/file_selector/example/test/widget_test.dart similarity index 100% rename from packages/file_picker/file_picker/example/test/widget_test.dart rename to packages/file_selector/file_selector/example/test/widget_test.dart diff --git a/packages/file_picker/file_picker/example/web/favicon.png b/packages/file_selector/file_selector/example/web/favicon.png similarity index 100% rename from packages/file_picker/file_picker/example/web/favicon.png rename to packages/file_selector/file_selector/example/web/favicon.png diff --git a/packages/file_picker/file_picker/example/web/icons/Icon-192.png b/packages/file_selector/file_selector/example/web/icons/Icon-192.png similarity index 100% rename from packages/file_picker/file_picker/example/web/icons/Icon-192.png rename to packages/file_selector/file_selector/example/web/icons/Icon-192.png diff --git a/packages/file_picker/file_picker/example/web/icons/Icon-512.png b/packages/file_selector/file_selector/example/web/icons/Icon-512.png similarity index 100% rename from packages/file_picker/file_picker/example/web/icons/Icon-512.png rename to packages/file_selector/file_selector/example/web/icons/Icon-512.png diff --git a/packages/file_picker/file_picker/example/web/index.html b/packages/file_selector/file_selector/example/web/index.html similarity index 100% rename from packages/file_picker/file_picker/example/web/index.html rename to packages/file_selector/file_selector/example/web/index.html diff --git a/packages/file_picker/file_picker/example/web/manifest.json b/packages/file_selector/file_selector/example/web/manifest.json similarity index 100% rename from packages/file_picker/file_picker/example/web/manifest.json rename to packages/file_selector/file_selector/example/web/manifest.json diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_selector/file_selector/lib/file_selector.dart similarity index 59% rename from packages/file_picker/file_picker/lib/file_picker.dart rename to packages/file_selector/file_selector/lib/file_selector.dart index 0857ecc11bb6..26b5f5404329 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -5,24 +5,24 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; -export 'package:file_picker_platform_interface/file_picker_platform_interface.dart' +export 'package:file_selector_platform_interface/file_selector_platform_interface.dart' show XFile, XTypeGroup, XType; /// NEW API /// Open file dialog for loading files and return a file path Future loadFile({List acceptedTypeGroups}) { - return FilePickerPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups); + return FileSelectorPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups); } /// Open file dialog for loading files and return a list of file paths Future> loadFiles({List acceptedTypeGroups}) { - return FilePickerPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups); + return FileSelectorPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups); } /// Saves File to user's file system Future getSavePath() async { - return FilePickerPlatform.instance.getSavePath(); + return FileSelectorPlatform.instance.getSavePath(); } \ No newline at end of file diff --git a/packages/file_picker/file_picker/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml similarity index 72% rename from packages/file_picker/file_picker/pubspec.yaml rename to packages/file_selector/file_selector/pubspec.yaml index 4679320c134a..ed3244de9aea 100644 --- a/packages/file_picker/file_picker/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -1,4 +1,4 @@ -name: file_picker +name: file_selector description: Flutter plugin for launching a URL on Android and iOS. Supports web, phone, SMS, and email schemes. homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher @@ -8,15 +8,15 @@ flutter: plugin: platforms: web: - default_package: file_picker_web + default_package: file_selector_web dependencies: flutter: sdk: flutter - file_picker_platform_interface: - path: ../file_picker_platform_interface - file_picker_web: - path: ../file_picker_web + file_selector_platform_interface: + path: ../file_selector_platform_interface + file_selector_web: + path: ../file_selector_web dev_dependencies: diff --git a/packages/file_picker/file_picker/test/file_picker_test.dart b/packages/file_selector/file_selector/test/file_picker_test.dart similarity index 100% rename from packages/file_picker/file_picker/test/file_picker_test.dart rename to packages/file_selector/file_selector/test/file_picker_test.dart diff --git a/packages/file_picker/file_picker_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md similarity index 100% rename from packages/file_picker/file_picker_platform_interface/CHANGELOG.md rename to packages/file_selector/file_selector_platform_interface/CHANGELOG.md diff --git a/packages/file_picker/file_picker_platform_interface/LICENSE b/packages/file_selector/file_selector_platform_interface/LICENSE similarity index 100% rename from packages/file_picker/file_picker_platform_interface/LICENSE rename to packages/file_selector/file_selector_platform_interface/LICENSE diff --git a/packages/file_picker/file_picker_platform_interface/README.md b/packages/file_selector/file_selector_platform_interface/README.md similarity index 100% rename from packages/file_picker/file_picker_platform_interface/README.md rename to packages/file_selector/file_selector_platform_interface/README.md diff --git a/packages/file_selector/file_selector_platform_interface/lib/file_selector_platform_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/file_selector_platform_interface.dart new file mode 100644 index 000000000000..69e3064150b5 --- /dev/null +++ b/packages/file_selector/file_selector_platform_interface/lib/file_selector_platform_interface.dart @@ -0,0 +1,2 @@ +export 'src/platform_interface/file_selector_interface.dart'; +export 'src/types/types.dart'; diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart similarity index 79% rename from packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index 5cdcf5d86fdb..bfaccb790c0f 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -6,13 +6,13 @@ import 'dart:typed_data'; import 'package:flutter/services.dart'; -import '../platform_interface/file_picker_interface.dart'; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; +import '../platform_interface/file_selector_interface.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); -/// An implementation of [FilePickerPlatform] that uses method channels. -class MethodChannelFilePicker extends FilePickerPlatform { +/// An implementation of [FileSelectorPlatform] that uses method channels. +class MethodChannelFileSelector extends FileSelectorPlatform { /// Load a file from user's computer and return it as an XFile @override Future loadFile({List acceptedTypeGroups}) { diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart similarity index 68% rename from packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index 260def18f868..b2d526e7e8f8 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -5,10 +5,10 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import '../method_channel/method_channel_file_picker.dart'; +import '../method_channel/method_channel_file_selector.dart'; @@ -19,23 +19,23 @@ import '../method_channel/method_channel_file_picker.dart'; /// 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 -/// [FilePickerPlatform] methods. -abstract class FilePickerPlatform extends PlatformInterface { - /// Constructs a FilePickerPlatform. - FilePickerPlatform() : super(token: _token); +/// [FileSelectorPlatform] methods. +abstract class FileSelectorPlatform extends PlatformInterface { + /// Constructs a FileSelectorPlatform. + FileSelectorPlatform() : super(token: _token); static final Object _token = Object(); - static FilePickerPlatform _instance = MethodChannelFilePicker(); + static FileSelectorPlatform _instance = MethodChannelFileSelector(); - /// The default instance of [FilePickerPlatform] to use. + /// The default instance of [FileSelectorPlatform] to use. /// - /// Defaults to [MethodChannelFilePicker]. - static FilePickerPlatform get instance => _instance; + /// Defaults to [MethodChannelFileSelector]. + static FileSelectorPlatform get instance => _instance; /// Platform-specific plugins should set this with their own platform-specific - /// class that extends [FilePickerPlatform] when they register themselves. - static set instance(FilePickerPlatform instance) { + /// class that extends [FileSelectorPlatform] when they register themselves. + static set instance(FileSelectorPlatform instance) { PlatformInterface.verifyToken(instance, _token); _instance = instance; } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_type/x_type.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/x_type/x_type.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart diff --git a/packages/file_picker/file_picker_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml similarity index 94% rename from packages/file_picker/file_picker_platform_interface/pubspec.yaml rename to packages/file_selector/file_selector_platform_interface/pubspec.yaml index 9b8de97e38a6..71aa2f06d2b6 100644 --- a/packages/file_picker/file_picker_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -1,4 +1,4 @@ -name: file_picker_platform_interface +name: file_selector_platform_interface description: A common platform interface for the file_picker plugin. homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_platform_interface # NOTE: We strongly prefer non-breaking changes, even at the expense of a diff --git a/packages/file_picker/file_picker_platform_interface/test/file_picker_platform_interface_test.dart b/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart similarity index 91% rename from packages/file_picker/file_picker_platform_interface/test/file_picker_platform_interface_test.dart rename to packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart index fd5dd8deea2a..e454ac6ca951 100644 --- a/packages/file_picker/file_picker_platform_interface/test/file_picker_platform_interface_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart @@ -7,8 +7,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; -import 'package:file_picker_platform_interface/src/method_channel/method_channel_file_picker.dart'; +import 'package:file_picker_platform_interface/file_selector_platform_interface.dart'; +import 'package:file_picker_platform_interface/src/method_channel/method_channel_file_selector.dart'; void main() { group('$FilePickerPlatform', () { diff --git a/packages/file_picker/file_picker_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md similarity index 100% rename from packages/file_picker/file_picker_web/CHANGELOG.md rename to packages/file_selector/file_selector_web/CHANGELOG.md diff --git a/packages/file_picker/file_picker_web/LICENSE b/packages/file_selector/file_selector_web/LICENSE similarity index 100% rename from packages/file_picker/file_picker_web/LICENSE rename to packages/file_selector/file_selector_web/LICENSE diff --git a/packages/file_picker/file_picker_web/README.md b/packages/file_selector/file_selector_web/README.md similarity index 100% rename from packages/file_picker/file_picker_web/README.md rename to packages/file_selector/file_selector_web/README.md diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart similarity index 87% rename from packages/file_picker/file_picker_web/lib/file_picker_web.dart rename to packages/file_selector/file_selector_web/lib/file_selector_web.dart index a9f58bbf0f54..46d97059fc5b 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -2,32 +2,32 @@ import 'dart:async'; import 'dart:html'; import 'dart:typed_data'; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:meta/meta.dart'; -final String _kFilePickerInputsDomId = '__file_picker_web-file-input'; +final String _kFileSelectorInputsDomId = '__file_selector_web-file-input'; -/// The web implementation of [FilePickerPlatform]. +/// The web implementation of [FileSelectorPlatform]. /// -/// This class implements the `package:file_picker` functionality for the web. -class FilePickerPlugin extends FilePickerPlatform { +/// This class implements the `package:file_selector` functionality for the web. +class FileSelectorPlugin extends FileSelectorPlatform { Element _target; - final FilePickerPluginTestOverrides _overrides; + final FileSelectorPluginTestOverrides _overrides; bool get _hasTestOverrides => _overrides != null; /// Default constructor, initializes _target to a DOM element that we can use /// to host HTML elements. /// overrides parameter allows for testing to override functions - FilePickerPlugin({ - @visibleForTesting FilePickerPluginTestOverrides overrides, + FileSelectorPlugin({ + @visibleForTesting FileSelectorPluginTestOverrides overrides, }) : _overrides = overrides { - _target = _ensureInitialized(_kFilePickerInputsDomId); + _target = _ensureInitialized(_kFileSelectorInputsDomId); } - /// Registers this class as the default instance of [FilePickerPlatform]. + /// Registers this class as the default instance of [FileSelectorPlatform]. static void registerWith(Registrar registrar) { - FilePickerPlatform.instance = FilePickerPlugin(); + FileSelectorPlatform.instance = FileSelectorPlugin(); } /// Convert list of filter groups to a comma-separated string @@ -184,7 +184,7 @@ class FilePickerPlugin extends FilePickerPlatform { /// Overrides some functions to allow testing @visibleForTesting -class FilePickerPluginTestOverrides { +class FileSelectorPluginTestOverrides { /// For overriding the creation of the file input element. Element Function(String accepted) createFileInputElement; diff --git a/packages/file_picker/file_picker_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml similarity index 68% rename from packages/file_picker/file_picker_web/pubspec.yaml rename to packages/file_selector/file_selector_web/pubspec.yaml index 57cde59325a3..033bef65a459 100644 --- a/packages/file_picker/file_picker_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -1,6 +1,6 @@ -name: file_picker_web -description: Web platform implementation of file_picker -homepage: https://github.com/flutter/plugins/tree/master/packages/file_picker/file_picker +name: file_selector_web +description: Web platform implementation of file_selector +homepage: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector # 0.1.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 @@ -10,12 +10,12 @@ flutter: plugin: platforms: web: - pluginClass: FilePickerPlugin - fileName: file_picker_web.dart + pluginClass: FileSelectorPlugin + fileName: file_selector_web.dart dependencies: - file_picker_platform_interface: - path: ../file_picker_platform_interface + file_selector_platform_interface: + path: ../file_selector_platform_interface platform_detect: ^1.4.0 flutter: sdk: flutter diff --git a/packages/file_picker/file_picker_web/test/README.md b/packages/file_selector/file_selector_web/test/README.md similarity index 100% rename from packages/file_picker/file_picker_web/test/README.md rename to packages/file_selector/file_selector_web/test/README.md diff --git a/packages/file_picker/file_picker_web/test/lib/main.dart b/packages/file_selector/file_selector_web/test/lib/main.dart similarity index 100% rename from packages/file_picker/file_picker_web/test/lib/main.dart rename to packages/file_selector/file_selector_web/test/lib/main.dart diff --git a/packages/file_picker/file_picker_web/test/pubspec.yaml b/packages/file_selector/file_selector_web/test/pubspec.yaml similarity index 100% rename from packages/file_picker/file_picker_web/test/pubspec.yaml rename to packages/file_selector/file_selector_web/test/pubspec.yaml diff --git a/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart similarity index 98% rename from packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart rename to packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index 6442507f0f6e..d5bb4343cfe1 100644 --- a/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -9,7 +9,7 @@ import 'dart:convert'; import 'dart:typed_data'; import 'dart:html'; -import 'package:file_picker_web/file_picker_web.dart'; +import 'package:file_picker_web/file_selector_web.dart'; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; import 'package:platform_detect/test_utils.dart' as platform; diff --git a/packages/file_picker/file_picker_web/test/test_driver/web_e2e_test.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e_test.dart similarity index 100% rename from packages/file_picker/file_picker_web/test/test_driver/web_e2e_test.dart rename to packages/file_selector/file_selector_web/test/test_driver/web_e2e_test.dart diff --git a/packages/file_picker/file_picker_web/test/web/index.html b/packages/file_selector/file_selector_web/test/web/index.html similarity index 100% rename from packages/file_picker/file_picker_web/test/web/index.html rename to packages/file_selector/file_selector_web/test/web/index.html From 26457dbedca56e3538573aa0c6814d9a916df5b7 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 18 Aug 2020 17:50:14 -0700 Subject: [PATCH 034/151] Update example app --- packages/file_selector/file_selector/example/lib/main.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 2ae04cfb2b4e..ea9b021db069 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -143,6 +143,3 @@ class _MyHomePageState extends State { ); } } - -class XPath { -} From bfc2f6a1eada65dd1ecef2cd8926edad5ddfc038 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 19 Aug 2020 17:41:21 -0700 Subject: [PATCH 035/151] Add lastModified with time zone adjustment --- .../lib/src/types/x_file/html.dart | 3 +++ .../lib/src/types/x_file/io.dart | 2 ++ .../file_selector_web/lib/file_selector_web.dart | 6 ++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index c1cc415c739f..799fab793444 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -19,6 +19,7 @@ class XFile extends XFileBase { final Uint8List _data; final int _length; final String name; + final DateTime lastModified; Element _target; /// Construct a XFile object from its ObjectUrl. @@ -34,6 +35,7 @@ class XFile extends XFileBase { this.name, int length, Uint8List bytes, + this.lastModified, }) : _data = bytes, _length = length, super(path) { @@ -47,6 +49,7 @@ class XFile extends XFileBase { this.type, this.name, int length, + this.lastModified, }) : _data = bytes, _length = length, super('') { diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart index 3538e6364804..dbb0706ab932 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart @@ -15,6 +15,8 @@ class XFile extends XFileBase { final XType type; + DateTime get lastModified => _file.lastModifiedSync(); + /// Construct a XFile object backed by a dart:io File. XFile(String path, { this.type }) : _file = File(path), diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 46d97059fc5b..6532981dff4c 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -71,13 +71,15 @@ class FileSelectorPlugin extends FileSelectorPlatform { List _getXFilesFromFiles (List files) { List xFiles = List(); + Duration timeZoneOffset = DateTime.now().timeZoneOffset; + for (File file in files) { String url = Url.createObjectUrl(file); String name = file.name; int length = file.size; - int modified = file.lastModified; + DateTime modified = file.lastModifiedDate.add(timeZoneOffset); - xFiles.add(XFile(url, name: name)); + xFiles.add(XFile(url, name: name, lastModified: modified)); } return xFiles; From 1538a74743b0ad00b14550c93bc86a51f31ca51a Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Thu, 20 Aug 2020 13:39:45 -0700 Subject: [PATCH 036/151] Update API to include non-web supported features --- .../file_selector/lib/file_selector.dart | 15 ++++++++++++--- .../method_channel_file_selector.dart | 15 ++++++++++++--- .../file_selector_interface.dart | 15 ++++++++++++--- .../lib/file_selector_web.dart | 19 ++++++++++++++----- 4 files changed, 50 insertions(+), 14 deletions(-) diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index 26b5f5404329..de704e4b7e97 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -13,16 +13,25 @@ export 'package:file_selector_platform_interface/file_selector_platform_interfac /// NEW API /// Open file dialog for loading files and return a file path -Future loadFile({List acceptedTypeGroups}) { +Future loadFile({ + List acceptedTypeGroups, + String initialDirectory, +}) { return FileSelectorPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups); } /// Open file dialog for loading files and return a list of file paths -Future> loadFiles({List acceptedTypeGroups}) { +Future> loadFiles({ + List acceptedTypeGroups, + String initialDirectory, +}) { return FileSelectorPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups); } /// Saves File to user's file system -Future getSavePath() async { +Future getSavePath({ + String initialDirectory, + String suggestedName, +}) async { return FileSelectorPlatform.instance.getSavePath(); } \ No newline at end of file diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index bfaccb790c0f..6f8f8357d46a 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -15,7 +15,10 @@ const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); class MethodChannelFileSelector extends FileSelectorPlatform { /// Load a file from user's computer and return it as an XFile @override - Future loadFile({List acceptedTypeGroups}) { + Future loadFile({ + List acceptedTypeGroups, + String initialDirectory, + }) { return _channel.invokeMethod( 'loadFile', { @@ -26,7 +29,10 @@ class MethodChannelFileSelector extends FileSelectorPlatform { /// Load multiple files from user's computer and return it as an XFile @override - Future> loadFiles({List acceptedTypeGroups}) { + Future> loadFiles({ + List acceptedTypeGroups, + String initialDirectory, + }) { return _channel.invokeMethod>( 'loadFiles', { @@ -37,7 +43,10 @@ class MethodChannelFileSelector extends FileSelectorPlatform { /// Saves the file to user's Disk @override - Future getSavePath() async { + Future getSavePath({ + String initialDirectory, + String suggestedName, + }) async { return _channel.invokeMethod( 'saveFile', { diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index b2d526e7e8f8..94191fea7a0b 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -41,17 +41,26 @@ abstract class FileSelectorPlatform extends PlatformInterface { } /// Open file dialog for loading files and return a file path - Future loadFile({List acceptedTypeGroups}) { + Future loadFile({ + List acceptedTypeGroups, + String initialDirectory, + }) { throw UnimplementedError('loadFile() has not been implemented.'); } /// Open file dialog for loading files and return a list of file paths - Future> loadFiles({List acceptedTypeGroups}) { + Future> loadFiles({ + List acceptedTypeGroups, + String initialDirectory, + }) { throw UnimplementedError('loadFile() has not been implemented.'); } /// Open file dialog for saving files and return a file path at which to save - Future getSavePath() { + Future getSavePath({ + String initialDirectory, + String suggestedName, + }) { throw UnimplementedError('saveFile() has not been implemented.'); } } diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 6532981dff4c..8cd0a754cdae 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -160,9 +160,12 @@ class FileSelectorPlugin extends FileSelectorPlatform { return _getFilesWhenReady(element); } - /// Open file dialog for loading files and return a file path + /// Open file dialog for loading files and return a XFile @override - Future loadFile({List acceptedTypeGroups}) { + Future loadFile({ + List acceptedTypeGroups, + String initialDirectory, + }) { Completer _completer = Completer(); _loadFileHelper(false, acceptedTypeGroups).then((list) { _completer.complete(list.first); @@ -174,14 +177,20 @@ class FileSelectorPlugin extends FileSelectorPlatform { return _completer.future; } - /// Open file dialog for loading files and return a list of file paths + /// Open file dialog for loading files and return a XFile @override - Future> loadFiles({List acceptedTypeGroups}) { + Future> loadFiles({ + List acceptedTypeGroups, + String initialDirectory, + }) { return _loadFileHelper(true, acceptedTypeGroups); } @override - Future getSavePath() => Future.value(); + Future getSavePath({ + String initialDirectory, + String suggestedName, + }) => Future.value(); } /// Overrides some functions to allow testing From bcdc562b8746be24e0a48ddc0f203d1bf9b042c9 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Thu, 20 Aug 2020 14:09:55 -0700 Subject: [PATCH 037/151] Update Example App - Separate load and save --- .../file_selector/example/lib/main.dart | 138 +++++++++++++++--- 1 file changed, 121 insertions(+), 17 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index ea9b021db069..e5be679c0515 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -12,31 +12,32 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter Demo', + title: 'File Selector Demo', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(title: 'File Selector Demo Home Page'), + routes: { + '/save' : (context) => SaveTest(title: "Save Example"), + '/load' : (context) => LoadTest(title: "Load Example"), + }, ); } } -/// Home Page of the application -class MyHomePage extends StatefulWidget { - - /// Constructor for MyHomePage - MyHomePage({Key key, this.title}) : super(key: key); + +class SaveTest extends StatefulWidget { + SaveTest({Key key, this.title}) : super(key: key); /// Title of Home Page final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + _SaveTestState createState() => _SaveTestState(); } -/// State of Home Page -class _MyHomePageState extends State { +class _SaveTestState extends State { final TextEditingController _fileController = TextEditingController(); final TextEditingController _nameController = TextEditingController(); final TextEditingController _extensionController = TextEditingController(); @@ -63,6 +64,67 @@ class _MyHomePageState extends State { new_file.saveTo(''); } + @override + Widget build(BuildContext context) { + + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _nameController, + decoration: InputDecoration( + hintText: '(Optional) Suggest File Name', + ), + ), + ), + Container( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _fileController, + decoration: InputDecoration( + hintText: 'Enter File Contents', + ), + ), + ), + SizedBox(height: 10), + RaisedButton( + child: Text('Press to save file'), + onPressed: () => { _saveFile() }, + ), + ], + ), + ), + ); + } +} + +class LoadTest extends StatefulWidget { + LoadTest({Key key, this.title}) : super(key: key); + + /// Title of Home Page + final String title; + + + @override + _LoadTestState createState() => _LoadTestState(); +} + +class _LoadTestState extends State { + final TextEditingController _fileController = TextEditingController(); + final TextEditingController _nameController = TextEditingController(); + final TextEditingController _extensionController = TextEditingController(); + void _loadFile() async { XFile file; if (_extensionController.text.isNotEmpty) { @@ -88,7 +150,6 @@ class _MyHomePageState extends State { @override Widget build(BuildContext context) { - return Scaffold( appBar: AppBar( title: Text(widget.title), @@ -100,30 +161,27 @@ class _MyHomePageState extends State { Container( width: 300, child: TextField( + enabled: false, minLines: 1, maxLines: 12, controller: _nameController, decoration: InputDecoration( - hintText: '(Optional) Suggest File Name', + hintText: 'File Name Will Appear Here', ), ), ), Container( width: 300, child: TextField( + enabled: false, minLines: 1, maxLines: 12, controller: _fileController, decoration: InputDecoration( - hintText: 'Enter File Contents', + hintText: 'File Contents Will Appear Here', ), ), ), - SizedBox(height: 10), - RaisedButton( - child: Text('Press to save file'), - onPressed: () => { _saveFile() }, - ), Container( width: 300, child: TextField( @@ -143,3 +201,49 @@ class _MyHomePageState extends State { ); } } + + + +/// Home Page of the application +class MyHomePage extends StatefulWidget { + + /// Constructor for MyHomePage + MyHomePage({Key key, this.title}) : super(key: key); + + /// Title of Home Page + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +/// State of Home Page +class _MyHomePageState extends State { + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + + SizedBox(height: 10), + RaisedButton( + child: Text('Press to try saving a file'), + onPressed: () => Navigator.pushNamed(context, '/save'), + ), + RaisedButton( + child: Text('Press to try loading a file'), + onPressed: () => Navigator.pushNamed(context, '/load'), + ), + ], + ), + ), + ); + } + +} From 1726804f3bbe3b37b6d908bcee420760de44a588 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Thu, 20 Aug 2020 18:03:28 -0700 Subject: [PATCH 038/151] Load Images and Text Files in Example + Bug Fixes Bug Fix in XFile, querySelector requires valid CSS selector, which file names are not. Bug Fix in XType, extensions that start with '.' were not able to be translated to MIME. --- .../file_selector/example/lib/main.dart | 179 ++++++++++++------ .../lib/src/types/x_file/html.dart | 7 +- .../lib/src/types/x_type/x_type.dart | 7 +- 3 files changed, 132 insertions(+), 61 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index e5be679c0515..4d55d424dc21 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -26,7 +26,7 @@ class MyApp extends StatelessWidget { } } - +/// Page for showing an example of saving with file_selector class SaveTest extends StatefulWidget { SaveTest({Key key, this.title}) : super(key: key); @@ -49,6 +49,8 @@ class _SaveTestState extends State { } void _saveFile() async { + String path = await getSavePath(); + Uint8List data; data = Uint8List.fromList(_fileController.text.codeUnits); @@ -61,7 +63,7 @@ class _SaveTestState extends State { new_file = XFile.fromData(data, type: type, name: _nameController.text); } - new_file.saveTo(''); + new_file.saveTo(path); } @override @@ -99,7 +101,7 @@ class _SaveTestState extends State { ), SizedBox(height: 10), RaisedButton( - child: Text('Press to save file'), + child: Text('Press to save a text file'), onPressed: () => { _saveFile() }, ), ], @@ -109,7 +111,9 @@ class _SaveTestState extends State { } } +/// Screen that shows an example of loadFile(s) class LoadTest extends StatefulWidget { + /// Default constructor LoadTest({Key key, this.title}) : super(key: key); /// Title of Home Page @@ -121,31 +125,38 @@ class LoadTest extends StatefulWidget { } class _LoadTestState extends State { - final TextEditingController _fileController = TextEditingController(); - final TextEditingController _nameController = TextEditingController(); final TextEditingController _extensionController = TextEditingController(); - void _loadFile() async { - XFile file; - if (_extensionController.text.isNotEmpty) { - List typeGroups = List(); + void _onLoadImageFile() async { + XType jpg = XType(extension: '.jpg'); + XType png = XType(extension: '.png'); + XTypeGroup typeGroup = XTypeGroup(label: 'images', fileTypes: [ jpg, png ]); - List types = List(); - _extensionController.text.split(',').forEach((type) => types.add(XType(extension: type))); + XFile file = await loadFile(acceptedTypeGroups: [ typeGroup ]); - typeGroups.add(XTypeGroup(label: 'Example Files', fileTypes: types)); - file = await loadFile(acceptedTypeGroups: typeGroups); - } else { - file = await loadFile(); - } + await showDialog( + context: context, + builder: (context) { + return ImageDisplay(file: file); + } + ); - String text = await file.readAsString(); - _fileController.text = text; + } - if (file.name.isNotEmpty) { - _nameController.text = file.name; - } + void _onLoadTextFile() async { + XType txt = XType(extension: '.txt'); + XType json = XType(extension: '.json'); + XTypeGroup typeGroup = XTypeGroup(label: 'images', fileTypes: [ txt, json ]); + + XFile file = await loadFile(acceptedTypeGroups: [typeGroup]); + + await showDialog( + context: context, + builder: (context) { + return TextDisplay(file: file); + } + ); } @override @@ -158,42 +169,13 @@ class _LoadTestState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Container( - width: 300, - child: TextField( - enabled: false, - minLines: 1, - maxLines: 12, - controller: _nameController, - decoration: InputDecoration( - hintText: 'File Name Will Appear Here', - ), - ), - ), - Container( - width: 300, - child: TextField( - enabled: false, - minLines: 1, - maxLines: 12, - controller: _fileController, - decoration: InputDecoration( - hintText: 'File Contents Will Appear Here', - ), - ), - ), - Container( - width: 300, - child: TextField( - controller: _extensionController, - decoration: InputDecoration( - hintText: '(Optional) Accepted Load Extensions', - ), - ), + RaisedButton( + child: Text('Press to load an image file(png, jpg)'), + onPressed: () => _onLoadImageFile(), ), RaisedButton( - child: Text('Press to load a file'), - onPressed: () => { _loadFile() }, + child: Text('Press to load a text file (json, txt)'), + onPressed: () => _onLoadTextFile(), ), ], ), @@ -202,6 +184,92 @@ class _LoadTestState extends State { } } +/// Widget that displays a text file in a dialog +class TextDisplay extends StatefulWidget { + /// File to display + final XFile file; + + /// Default Constructor + TextDisplay({Key key, @required this.file}) : super(key: key); + + @override + _TextDisplayState createState() => _TextDisplayState(); +} + +class _TextDisplayState extends State { + String fileContents; + + @override + void initState() { + super.initState(); + _getFileContents(); + } + + void _getFileContents() async { + String contents = await widget.file.readAsString(); + setState(() => fileContents = contents); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(widget.file.name), + content: Scrollbar( + child: SingleChildScrollView( + child: Text( + fileContents ?? 'Loading file contents...\nThis may take a while if your file is large.', + ), + ), + ), + actions: [ + FlatButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} + +/// Widget that displays a text file in a dialog +class ImageDisplay extends StatefulWidget { + /// File to display + final XFile file; + + /// Default Constructor + ImageDisplay({Key key, @required this.file}) : super(key: key); + + @override + _ImageDisplayState createState() => _ImageDisplayState(); +} + +class _ImageDisplayState extends State { + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(widget.file.name), + content: Image.network(widget.file.path), + + actions: [ + FlatButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} + /// Home Page of the application @@ -230,7 +298,6 @@ class _MyHomePageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - SizedBox(height: 10), RaisedButton( child: Text('Press to try saving a file'), diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index 799fab793444..1eea5117a6ca 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -39,8 +39,6 @@ class XFile extends XFileBase { }) : _data = bytes, _length = length, super(path) { - // Create a DOM container where we can host the anchor. - _target = _ensureInitialized(this.name + '-x-file-dom-element'); } /// Construct an XFile from its data @@ -60,8 +58,6 @@ class XFile extends XFileBase { blob = Blob([bytes], type.mime); } this.path = Url.createObjectUrl(blob); - // Create a DOM container where we can host the anchor. - _target = _ensureInitialized(this.name + '-x-file-dom-element'); } @@ -97,6 +93,9 @@ class XFile extends XFileBase { /// Saves the data of this XFile at the location indicated by path. /// For the web implementation, the path variable is ignored. void saveTo(String path) async { + // Create a DOM container where we can host the anchor. + _target = _ensureInitialized('-x-file-dom-element'); + // Create an tag with the appropriate download attributes and click it final AnchorElement element = createAnchorElement(this.path, this.name); diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart index ace2f602d45b..77947f13fde2 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart @@ -41,7 +41,12 @@ class XType { String extension, String mime, }) : this._extension = extension, - this._mime = mime; + this._mime = mime + { + if (_extension != null && _extension[0] == '.') { + _extension = _extension.substring(1); + } + } /// Get the mime type from this XType String get mime { From 0852e9444381d7081e811e67af56d67d26abc5b2 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 21 Aug 2020 14:58:37 -0700 Subject: [PATCH 039/151] API tests --- .../example/test/widget_test.dart | 15 ---- .../file_selector/lib/file_selector.dart | 6 +- .../file_selector/test/file_picker_test.dart | 8 --- .../test/file_selector_test.dart | 69 +++++++++++++++++++ 4 files changed, 72 insertions(+), 26 deletions(-) delete mode 100644 packages/file_selector/file_selector/test/file_picker_test.dart create mode 100644 packages/file_selector/file_selector/test/file_selector_test.dart diff --git a/packages/file_selector/file_selector/example/test/widget_test.dart b/packages/file_selector/file_selector/example/test/widget_test.dart index 747db1da35e8..c9a60e1f271f 100644 --- a/packages/file_selector/file_selector/example/test/widget_test.dart +++ b/packages/file_selector/file_selector/example/test/widget_test.dart @@ -11,20 +11,5 @@ 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); - }); } diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index de704e4b7e97..1da903db6ee1 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -17,7 +17,7 @@ Future loadFile({ List acceptedTypeGroups, String initialDirectory, }) { - return FileSelectorPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups); + return FileSelectorPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory); } /// Open file dialog for loading files and return a list of file paths @@ -25,7 +25,7 @@ Future> loadFiles({ List acceptedTypeGroups, String initialDirectory, }) { - return FileSelectorPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups); + return FileSelectorPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory); } /// Saves File to user's file system @@ -33,5 +33,5 @@ Future getSavePath({ String initialDirectory, String suggestedName, }) async { - return FileSelectorPlatform.instance.getSavePath(); + return FileSelectorPlatform.instance.getSavePath(initialDirectory: initialDirectory, suggestedName: suggestedName); } \ No newline at end of file diff --git a/packages/file_selector/file_selector/test/file_picker_test.dart b/packages/file_selector/file_selector/test/file_picker_test.dart deleted file mode 100644 index 6940fcfb4a6e..000000000000 --- a/packages/file_selector/file_selector/test/file_picker_test.dart +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -void main() { -} - diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart new file mode 100644 index 000000000000..85cf78cbe776 --- /dev/null +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -0,0 +1,69 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/material.dart'; +import 'package:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:file_selector/file_selector.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; + +void main() { + final MockFileSelector mock = MockFileSelector(); + FileSelectorPlatform.instance = mock; + + test('getSavePath', () async { + String expectedPath = '/example/path'; + + when( + mock.getSavePath( + initialDirectory: 'dir', + suggestedName: 'name', + ) + ).thenAnswer((_) => Future.value(expectedPath)); + + String result = await getSavePath(initialDirectory: 'dir', suggestedName: 'name'); + + expect(result, expectedPath); + }); + + test('loadFile', () async { + XFile file = XFile('path'); + + XTypeGroup typeGroup = XTypeGroup(label: 'group', fileTypes: [ XType(extension: '.json', mime: 'application/json') ]); + + when( + mock.loadFile( + acceptedTypeGroups: [typeGroup], + initialDirectory: 'dir', + ) + ).thenAnswer((_) => Future.value(file)); + + XFile result = await loadFile(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir'); + + expect(result, isNotNull); + }); + + test('loadFiles', () async { + XFile file = XFile('path'); + + XTypeGroup typeGroup = XTypeGroup(label: 'group', fileTypes: [ XType(extension: '.json', mime: 'application/json') ]); + + when( + mock.loadFiles( + acceptedTypeGroups: [typeGroup], + initialDirectory: 'dir', + ) + ).thenAnswer((_) => Future.value([file])); + + List result = await loadFiles(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir'); + + expect(result, isNotNull); + }); +} + +class MockFileSelector extends Mock + with MockPlatformInterfaceMixin + implements FileSelectorPlatform {} \ No newline at end of file From e5584665ae3f6fc98350a29e964b0dadeb8f2e20 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 21 Aug 2020 18:06:52 -0700 Subject: [PATCH 040/151] Test DOM container and input element creation Remove old tests --- .../lib/file_selector_web.dart | 35 +++--- .../file_selector_web/test/pubspec.yaml | 6 +- .../test/test_driver/web_e2e.dart | 115 +++++------------- 3 files changed, 49 insertions(+), 107 deletions(-) diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 8cd0a754cdae..260f3490b45b 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -47,7 +47,8 @@ class FileSelectorPlugin extends FileSelectorPlatform { /// Creates a file input element with only the accept attribute @visibleForTesting FileUploadInputElement createFileInputElement(String accepted, bool multiple) { - if (_hasTestOverrides) { + if (_hasTestOverrides && _overrides.createFileInputElement != null) { + print ("createFileInputElement overridden"); return _overrides.createFileInputElement(accepted); } @@ -88,30 +89,23 @@ class FileSelectorPlugin extends FileSelectorPlatform { /// Getter for retrieving files from an input element @visibleForTesting List getFilesFromInputElement(InputElement element) { - if(_hasTestOverrides) { + if(_hasTestOverrides && _overrides.getFilesFromInputElement != null) { + print ("getFilesFromInputElement overridden"); return _overrides.getFilesFromInputElement(element); } return element?.files ?? []; } - - Future _getFileWhenReady(InputElement element) { - final Completer _completer = Completer(); - - _getFilesWhenReady(element) - .then((list) { - _completer.complete(list[0]); - }) - .catchError((err) { - _completer.completeError(err); - }); - - return _completer.future; - } /// Listen for file input element to change and retrieve files when /// this happens. - Future> _getFilesWhenReady(InputElement element) { + @visibleForTesting + Future> getFilesWhenReady(InputElement element) { + if(_hasTestOverrides && _overrides.getFilesWhenReady != null) { + print ("getFilesWhenReady overridden"); + return _overrides.getFilesWhenReady(element); + } + final Completer> _completer = Completer(); // Listens for element change @@ -157,7 +151,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { _addElementToDomAndClick(element); - return _getFilesWhenReady(element); + return getFilesWhenReady(element); } /// Open file dialog for loading files and return a XFile @@ -201,4 +195,9 @@ class FileSelectorPluginTestOverrides { /// For overriding retrieving a file from the input element. List Function(InputElement input) getFilesFromInputElement; + + /// For overriding waiting for the files to be ready. Useful for testing so we do not hang here. + Future> Function(InputElement input) getFilesWhenReady; + + FileSelectorPluginTestOverrides({this.createFileInputElement, this.getFilesFromInputElement, this.getFilesWhenReady}); } \ No newline at end of file diff --git a/packages/file_selector/file_selector_web/test/pubspec.yaml b/packages/file_selector/file_selector_web/test/pubspec.yaml index c59457c1d9d6..12bbc2ac8720 100644 --- a/packages/file_selector/file_selector_web/test/pubspec.yaml +++ b/packages/file_selector/file_selector_web/test/pubspec.yaml @@ -9,10 +9,10 @@ dependencies: sdk: flutter dev_dependencies: - file_picker_web: + file_selector_web: path: ../ - file_picker_platform_interface: - path: ../../file_picker_platform_interface + file_selector_platform_interface: + path: ../../file_selector_platform_interface google_maps: ^3.4.4 flutter_driver: sdk: flutter diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index d5bb4343cfe1..b4e21fcc8295 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -9,112 +9,55 @@ import 'dart:convert'; import 'dart:typed_data'; import 'dart:html'; -import 'package:file_picker_web/file_selector_web.dart'; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; +import 'package:file_selector_web/file_selector_web.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:platform_detect/test_utils.dart' as platform; -final String expectedStringContents = 'Hello, world!'; -final expectedSize = expectedStringContents.length; -final Uint8List bytes = utf8.encode(expectedStringContents); -final File textFile = File([bytes], 'hello.txt'); - -final String expectedStringContents2 = 'This is the other test file'; -final expectedSize2 = expectedStringContents.length; -final Uint8List bytes2 = utf8.encode(expectedStringContents); -final File textFile2 = File([bytes], 'test2.txt'); +import 'dart:developer'; +final String domElementId = '__file_selector_web-file-input'; /// Test Markers void main() { E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; - group('loadFile: ', () { - test('Select a single file to load', () async { - final mockInput = FileUploadInputElement(); - - // Note that we override the retrieval of files from the input element. - // We opt to do this because dart cannot edit the "files" attribute of - // tag. When performing mockInput.files = [ textFile ] we receive: - // "Failed to set the 'files' property on 'HTMLInputElement': The provided - // value is not of type 'FileList'." - // - // More on this (javascript side): https://stackoverflow.com/questions/52078853/is-it-possible-to-update-filelist - // The dart implementation will depend on the javascript it creates. - final plugin = FilePickerPlugin( - overrides: FilePickerPluginTestOverrides() - ..createFileInputElement = ((_) => mockInput) - ..getFilesFromInputElement = ((_) => [textFile]) - ); - - // Call load file - final files = plugin.loadFile(); - - // Mock selection of a file - mockInput.dispatchEvent(Event('change')); + FileSelectorPlugin plugin; - // Expect the file to complete - expect(files, completes); + testWidgets('Create a DOM container', (WidgetTester tester) { + plugin = FileSelectorPlugin(); - // Expect that we can read from the file - final loadedFiles = await files; - final loadedFile = loadedFiles.first; - expect(loadedFile.readAsBytes(), completion(isNotEmpty)); - expect(loadedFile.length(), completion(equals(expectedSize))); - expect(loadedFile.name, textFile.name); - }); - - test('Select multiple files to load', () async { - final mockInput = FileUploadInputElement(); + final result = querySelector('#${domElementId}'); + expect(result, isNotNull); + }); - final plugin = FilePickerPlugin( - overrides: FilePickerPluginTestOverrides() - ..createFileInputElement = ((_) => mockInput) - ..getFilesFromInputElement = ((_) => [textFile, textFile2]) + group('loadFile(..)', () { + testWidgets('creates correct input element', (WidgetTester tester) async { + final overrides = FileSelectorPluginTestOverrides( + getFilesWhenReady: (_) => Future.value([ XFile('path') ]), ); - // Call load file - final files = plugin.loadFile(); - - // Mock selection of a file - mockInput.dispatchEvent(Event('change')); + plugin = FileSelectorPlugin( + overrides: overrides, + ); - // Expect the file to complete - expect(files, completes); + final container = querySelector('#${domElementId}'); - // Expect that we can read from the files - final loadedFiles = await files; - final file1 = loadedFiles[0]; - expect(file1.readAsBytes(), completion(isNotEmpty)); - expect(file1.length(), completion(equals(expectedSize))); - expect(file1.name, textFile.name); + final typeGroup = XTypeGroup(label: 'test', + fileTypes: [ + XType(extension: 'json', mime: 'application/json'), + XType(extension: 'txt', mime: 'text/plain'), + ]); - final file2 = loadedFiles[1]; - expect(file2.readAsBytes(), completion(isNotEmpty)); - expect(file2.length(), completion(equals(expectedSize2))); - expect(file2.name, textFile2.name); - }); - }); + final file = await plugin.loadFile(acceptedTypeGroups: [ typeGroup ]); - group('saveFile: ', () { - test('Create a blob', () { - Uint8List data = Uint8List.fromList(expectedStringContents.codeUnits); - - FilePickerPlugin plugin = FilePickerPlugin(); - final blob = plugin.createBlob(data, 'text/plain'); - - expect(blob.type, 'text/plain'); - expect(blob.size, expectedSize); - }); + expect(file, isNotNull); - test('Create an anchor', () { - FilePickerPlugin plugin = FilePickerPlugin(); - final String href = 'https://google.com'; - final String name = 'file_name.txt'; - final anchor = plugin.createAnchorElement(href, name); + final result = container?.children?.firstWhere((element) => element.tagName == 'INPUT', orElse: () => null); - expect(anchor.download, name); - expect(anchor.href, href); + expect(result, isNotNull); + expect(result.getAttribute('type'), 'file'); + expect(result.getAttribute('accept'), '.json,.txt'); }); }); } From 6d6460c397e7406f7be6011bff40ebf422958382 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Sun, 23 Aug 2020 17:55:06 -0700 Subject: [PATCH 041/151] loadFile, loadFiles, and getSavePath tests --- .../lib/file_selector_web.dart | 13 +- .../test/test_driver/web_e2e.dart | 166 +++++++++++++++++- 2 files changed, 164 insertions(+), 15 deletions(-) diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 260f3490b45b..8b60dd57ee10 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -48,8 +48,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { @visibleForTesting FileUploadInputElement createFileInputElement(String accepted, bool multiple) { if (_hasTestOverrides && _overrides.createFileInputElement != null) { - print ("createFileInputElement overridden"); - return _overrides.createFileInputElement(accepted); + return _overrides.createFileInputElement(accepted, multiple); } final FileUploadInputElement element = FileUploadInputElement(); @@ -71,7 +70,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { List _getXFilesFromFiles (List files) { List xFiles = List(); - + Duration timeZoneOffset = DateTime.now().timeZoneOffset; for (File file in files) { @@ -90,7 +89,6 @@ class FileSelectorPlugin extends FileSelectorPlatform { @visibleForTesting List getFilesFromInputElement(InputElement element) { if(_hasTestOverrides && _overrides.getFilesFromInputElement != null) { - print ("getFilesFromInputElement overridden"); return _overrides.getFilesFromInputElement(element); } @@ -102,7 +100,6 @@ class FileSelectorPlugin extends FileSelectorPlatform { @visibleForTesting Future> getFilesWhenReady(InputElement element) { if(_hasTestOverrides && _overrides.getFilesWhenReady != null) { - print ("getFilesWhenReady overridden"); return _overrides.getFilesWhenReady(element); } @@ -114,9 +111,9 @@ class FileSelectorPlugin extends FileSelectorPlatform { final List files = getFilesFromInputElement(element); // Create XFile from dart:html Files - final returnPaths = _getXFilesFromFiles(files); + final xFiles = _getXFilesFromFiles(files); - _completer.complete(returnPaths); + _completer.complete(xFiles); }); element.onError.first.then((event) { @@ -191,7 +188,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { @visibleForTesting class FileSelectorPluginTestOverrides { /// For overriding the creation of the file input element. - Element Function(String accepted) createFileInputElement; + Element Function(String accepted, bool multiple) createFileInputElement; /// For overriding retrieving a file from the input element. List Function(InputElement input) getFilesFromInputElement; diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index b4e21fcc8295..c40b124ba551 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -18,6 +18,20 @@ import 'dart:developer'; final String domElementId = '__file_selector_web-file-input'; +final textGroup = XTypeGroup(label: 'test', + fileTypes: [ + XType(extension: 'json', mime: 'application/json'), + XType(extension: 'txt', mime: 'text/plain'), + ]); + +final String expectedStringContents = 'Hello, world!'; +final Uint8List bytes = utf8.encode(expectedStringContents); +final File textFile = File([bytes], 'hello.txt'); + +final String expectedStringContents2 = 'This is the other test file'; +final Uint8List bytes2 = utf8.encode(expectedStringContents2); +final File textFile2 = File([bytes2], 'test2.txt'); + /// Test Markers void main() { E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; @@ -43,13 +57,8 @@ void main() { final container = querySelector('#${domElementId}'); - final typeGroup = XTypeGroup(label: 'test', - fileTypes: [ - XType(extension: 'json', mime: 'application/json'), - XType(extension: 'txt', mime: 'text/plain'), - ]); - - final file = await plugin.loadFile(acceptedTypeGroups: [ typeGroup ]); + print("Expect message: 'File chooser dialog can only be shown with a user activation'"); + final file = await plugin.loadFile(acceptedTypeGroups: [ textGroup ]); expect(file, isNotNull); @@ -59,5 +68,148 @@ void main() { expect(result.getAttribute('type'), 'file'); expect(result.getAttribute('accept'), '.json,.txt'); }); + + testWidgets('input element is clicked', (WidgetTester tester) async { + final mockInput = FileUploadInputElement(); + + final overrides = FileSelectorPluginTestOverrides( + getFilesWhenReady: (_) => Future.value([ XFile('path') ]), + createFileInputElement: (_, __) => mockInput, + ); + + + + plugin = FileSelectorPlugin( + overrides: overrides, + ); + + bool clicked = false; + mockInput.onClick.listen((event) => clicked = true); + + print("Expect message: 'File chooser dialog can only be shown with a user activation'"); + final file = await plugin.loadFile(acceptedTypeGroups: [ textGroup ]); + + expect(clicked, true); + }); + + testWidgets('get XFile from input element', (WidgetTester tester) async { + final mockInput = FileUploadInputElement(); + + final overrides = FileSelectorPluginTestOverrides( + getFilesFromInputElement: (_) => [textFile], + createFileInputElement: (_, __) => mockInput, + ); + + plugin = FileSelectorPlugin( + overrides: overrides, + ); + + // Call load file + final file = plugin.loadFile(); + + // Mock selection of a file + mockInput.dispatchEvent(Event('change')); + + // Expect the file to complete + expect(file, completes); + + // Expect that we can read from the file + final loadedFile = await file; + final contents = await loadedFile.readAsString(); + expect(contents, expectedStringContents); + expect(loadedFile.name, textFile.name); + }); }); + + + group('loadFiles(..)', () { + testWidgets('creates correct input element', (WidgetTester tester) async { + final overrides = FileSelectorPluginTestOverrides( + getFilesWhenReady: (_) => Future.value([ XFile('path'), XFile('path2') ]), + ); + + plugin = FileSelectorPlugin( + overrides: overrides, + ); + + final container = querySelector('#${domElementId}'); + + print("Expect message: 'File chooser dialog can only be shown with a user activation'"); + final files = await plugin.loadFiles(acceptedTypeGroups: [ textGroup ]); + + expect(files, isNotNull); + + final FileUploadInputElement result = container?.children?.firstWhere((element) => element.tagName == 'INPUT', orElse: () => null); + + expect(result, isNotNull); + expect(result.getAttribute('type'), 'file'); + expect(result.getAttribute('accept'), '.json,.txt'); + expect(result.multiple, true); + }); + + testWidgets('input element is clicked', (WidgetTester tester) async { + final mockInput = FileUploadInputElement(); + + final overrides = FileSelectorPluginTestOverrides( + getFilesWhenReady: (_) => Future.value([ XFile('path') ]), + createFileInputElement: (_, __) => mockInput, + ); + + + + plugin = FileSelectorPlugin( + overrides: overrides, + ); + + bool clicked = false; + mockInput.onClick.listen((event) => clicked = true); + + print("Expect message: 'File chooser dialog can only be shown with a user activation'"); + final file = await plugin.loadFiles(acceptedTypeGroups: [ textGroup ]); + + expect(clicked, true); + }); + + testWidgets('get XFiles from input element', (WidgetTester tester) async { + final mockInput = FileUploadInputElement(); + + final overrides = FileSelectorPluginTestOverrides( + getFilesFromInputElement: (_) => [textFile, textFile2], + createFileInputElement: (_, __) => mockInput, + ); + + plugin = FileSelectorPlugin( + overrides: overrides, + ); + + // Call load file + final files = plugin.loadFiles(); + + // Mock selection of files + mockInput.dispatchEvent(Event('change')); + + // Expect the file to complete + expect(files, completes); + + // Expect that we can read from the file + final loadedFiles = await files; + final loadedFile1 = loadedFiles[0]; + final loadedFile2 = loadedFiles[1]; + + final contents = await loadedFile1.readAsString(); + expect(contents, expectedStringContents); + expect(loadedFile1.name, textFile.name); + + final contents2 = await loadedFile2.readAsString(); + expect(contents2, expectedStringContents2); + expect(loadedFile2.name, textFile2.name); + }); + }); + + testWidgets('getSavePath completes', (WidgetTester tester) async { + plugin = FileSelectorPlugin(); + final path = plugin.getSavePath(); + expect(path, completes); + }); + } From ea025a2bfa182af5e7c29889f20da60375ea9840 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 24 Aug 2020 11:19:15 -0700 Subject: [PATCH 042/151] XFile saveTo(..) tests --- .../lib/src/types/x_file/html.dart | 32 ++++++++-- .../lib/file_selector_web.dart | 2 +- .../test/test_driver/web_e2e.dart | 59 ++++++++++++++++--- 3 files changed, 78 insertions(+), 15 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index 1eea5117a6ca..321457193128 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -22,6 +22,10 @@ class XFile extends XFileBase { final DateTime lastModified; Element _target; + final XFileTestOverrides _overrides; + + bool get _hasTestOverrides => _overrides != null; + /// Construct a XFile object from its ObjectUrl. /// /// Optionally, this can be initialized with `bytes` and `length` @@ -36,10 +40,11 @@ class XFile extends XFileBase { int length, Uint8List bytes, this.lastModified, + @visibleForTesting XFileTestOverrides overrides, }) : _data = bytes, _length = length, - super(path) { - } + _overrides = overrides, + super(path); /// Construct an XFile from its data XFile.fromData( @@ -48,11 +53,13 @@ class XFile extends XFileBase { this.name, int length, this.lastModified, + @visibleForTesting XFileTestOverrides overrides, }) : _data = bytes, _length = length, + _overrides = overrides, super('') { Blob blob; - if (type.mime == null) { + if (type == null) { blob = Blob([bytes]); } else { blob = Blob([bytes], type.mime); @@ -94,7 +101,7 @@ class XFile extends XFileBase { /// For the web implementation, the path variable is ignored. void saveTo(String path) async { // Create a DOM container where we can host the anchor. - _target = _ensureInitialized('-x-file-dom-element'); + _target = _ensureInitialized('__x_file_dom_element'); // Create an tag with the appropriate download attributes and click it final AnchorElement element = createAnchorElement(this.path, this.name); @@ -105,6 +112,10 @@ class XFile extends XFileBase { /// Create anchor element with download attribute @visibleForTesting AnchorElement createAnchorElement(String href, String suggestedName) { + if (_hasTestOverrides && _overrides.createAnchorElement != null) { + return _overrides.createAnchorElement(href, suggestedName); + } + final element = AnchorElement(href: href); element.download = suggestedName; return element; @@ -123,11 +134,22 @@ class XFile extends XFileBase { var target = querySelector('#${id}'); if (target == null) { final Element targetElement = - Element.tag('flt-x-file-input')..id = id; + Element.tag('flt-x-file')..id = id; querySelector('body').children.add(targetElement); target = targetElement; } return target; } +} + + +/// Overrides some functions to allow testing +@visibleForTesting +class XFileTestOverrides { + /// For overriding the creation of the file input element. + Element Function(String href, String suggestedName) createAnchorElement; + + /// Default constructor for overrides + XFileTestOverrides({this.createAnchorElement}); } \ No newline at end of file diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 8b60dd57ee10..d98a8921df75 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -6,7 +6,7 @@ import 'package:file_selector_platform_interface/file_selector_platform_interfac import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:meta/meta.dart'; -final String _kFileSelectorInputsDomId = '__file_selector_web-file-input'; +final String _kFileSelectorInputsDomId = '__file_selector_web_file_input'; /// The web implementation of [FileSelectorPlatform]. /// diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index c40b124ba551..89bb1d0904f1 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -16,7 +16,8 @@ import 'package:platform_detect/test_utils.dart' as platform; import 'dart:developer'; -final String domElementId = '__file_selector_web-file-input'; +final String domElementId = '__file_selector_web_file_input'; +final String xFileDomElementId = '__x_file_dom_element'; final textGroup = XTypeGroup(label: 'test', fileTypes: [ @@ -36,6 +37,8 @@ final File textFile2 = File([bytes2], 'test2.txt'); void main() { E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; + print("Note that the following message can be ignored in the test output:\n'File chooser dialog can only be shown with a user activation'"); + FileSelectorPlugin plugin; testWidgets('Create a DOM container', (WidgetTester tester) { @@ -57,7 +60,6 @@ void main() { final container = querySelector('#${domElementId}'); - print("Expect message: 'File chooser dialog can only be shown with a user activation'"); final file = await plugin.loadFile(acceptedTypeGroups: [ textGroup ]); expect(file, isNotNull); @@ -76,9 +78,7 @@ void main() { getFilesWhenReady: (_) => Future.value([ XFile('path') ]), createFileInputElement: (_, __) => mockInput, ); - - - + plugin = FileSelectorPlugin( overrides: overrides, ); @@ -86,7 +86,6 @@ void main() { bool clicked = false; mockInput.onClick.listen((event) => clicked = true); - print("Expect message: 'File chooser dialog can only be shown with a user activation'"); final file = await plugin.loadFile(acceptedTypeGroups: [ textGroup ]); expect(clicked, true); @@ -134,7 +133,6 @@ void main() { final container = querySelector('#${domElementId}'); - print("Expect message: 'File chooser dialog can only be shown with a user activation'"); final files = await plugin.loadFiles(acceptedTypeGroups: [ textGroup ]); expect(files, isNotNull); @@ -164,7 +162,6 @@ void main() { bool clicked = false; mockInput.onClick.listen((event) => clicked = true); - print("Expect message: 'File chooser dialog can only be shown with a user activation'"); final file = await plugin.loadFiles(acceptedTypeGroups: [ textGroup ]); expect(clicked, true); @@ -195,7 +192,7 @@ void main() { final loadedFiles = await files; final loadedFile1 = loadedFiles[0]; final loadedFile2 = loadedFiles[1]; - + final contents = await loadedFile1.readAsString(); expect(contents, expectedStringContents); expect(loadedFile1.name, textFile.name); @@ -212,4 +209,48 @@ void main() { expect(path, completes); }); + group('XFile saveTo(..)', () { + testWidgets('creates a DOM container', (WidgetTester tester) async { + XFile file = XFile.fromData(bytes); + + await file.saveTo(''); + + final container = querySelector('#${xFileDomElementId}'); + + expect(container, isNotNull); + }); + + testWidgets('create anchor element', (WidgetTester tester) async { + XFile file = XFile.fromData(bytes, name: textFile.name); + + await file.saveTo('path'); + + final container = querySelector('#${xFileDomElementId}'); + final AnchorElement element = container?.children?.firstWhere((element) => element.tagName == 'A', orElse: () => null); + + expect(element, isNotNull); + expect(element.href, file.path); + expect(element.download, file.name); + }); + + testWidgets('anchor element is clicked', (WidgetTester tester) async { + final mockAnchor = AnchorElement(); + + XFileTestOverrides overrides = XFileTestOverrides( + createAnchorElement: (_,__) => mockAnchor, + ); + + XFile file = XFile.fromData(bytes, name: textFile.name, overrides: overrides); + + bool clicked = false; + mockAnchor.onClick.listen((event) => clicked = true); + + await file.saveTo('path'); + + expect(clicked, true); + }); + }); + + print("Note that the following message can be ignored in the test output:\n'File chooser dialog can only be shown with a user activation'"); + } From 8899684a0cd15922682fa65d599d5abc422c9b15 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 24 Aug 2020 14:23:14 -0700 Subject: [PATCH 043/151] update Method Channel --- .../lib/src/method_channel/method_channel_file_selector.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index 6f8f8357d46a..91ab40c9f840 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -23,6 +23,7 @@ class MethodChannelFileSelector extends FileSelectorPlatform { 'loadFile', { 'acceptedTypes': acceptedTypeGroups, + 'initialDirectory': initialDirectory, }, ); } @@ -37,6 +38,7 @@ class MethodChannelFileSelector extends FileSelectorPlatform { 'loadFiles', { 'acceptedTypes': acceptedTypeGroups, + 'initialDirectory': initialDirectory, }, ); } @@ -50,7 +52,8 @@ class MethodChannelFileSelector extends FileSelectorPlatform { return _channel.invokeMethod( 'saveFile', { - + 'initialDirectory': initialDirectory, + 'suggestedName': suggestedName, }, ); } From 8cc32cc80d9cf08c7d21322fe45060ccd35ef57c Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 24 Aug 2020 18:33:20 -0700 Subject: [PATCH 044/151] Enforce txt file in anchor (Example app) --- packages/file_selector/file_selector/example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 4d55d424dc21..80876af3960a 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -60,7 +60,7 @@ class _SaveTestState extends State { if (_nameController.text == '') { new_file = XFile.fromData(data, type: type); } else { - new_file = XFile.fromData(data, type: type, name: _nameController.text); + new_file = XFile.fromData(data, type: type, name: _nameController.text + '.txt'); } new_file.saveTo(path); From 1827c994d6adda0a693e2473f0a1030b2be323fb Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 25 Aug 2020 10:24:31 -0700 Subject: [PATCH 045/151] Accept extensions in web saving --- .../file_selector/example/lib/main.dart | 4 ++-- .../lib/src/types/x_file/html.dart | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 80876af3960a..f658e256ec34 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -55,12 +55,12 @@ class _SaveTestState extends State { data = Uint8List.fromList(_fileController.text.codeUnits); XFile new_file; - XType type = XType(extension: '.txt'); + XType type = XType(extension: '.txt', mime: 'plain/text'); if (_nameController.text == '') { new_file = XFile.fromData(data, type: type); } else { - new_file = XFile.fromData(data, type: type, name: _nameController.text + '.txt'); + new_file = XFile.fromData(data, type: type, name: _nameController.text); } new_file.saveTo(path); diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index 321457193128..f06333a185dc 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -59,7 +59,7 @@ class XFile extends XFileBase { _overrides = overrides, super('') { Blob blob; - if (type == null) { + if (type.mime == null) { blob = Blob([bytes]); } else { blob = Blob([bytes], type.mime); @@ -117,7 +117,17 @@ class XFile extends XFileBase { } final element = AnchorElement(href: href); - element.download = suggestedName; + + if (suggestedName == null) { + element.download = 'download'; + } else { + element.download = suggestedName; + } + + if (type.extension != null) { + element.download += '.' + type.extension; + } + return element; } From 228c5def595c7a1ccda247059bc59066abd67c88 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 25 Aug 2020 11:59:49 -0700 Subject: [PATCH 046/151] Include extensions in loadFile --- .../lib/src/types/x_type/x_type.dart | 7 +------ .../file_selector_web/lib/file_selector_web.dart | 11 ++++++++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart index 77947f13fde2..ace2f602d45b 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart @@ -41,12 +41,7 @@ class XType { String extension, String mime, }) : this._extension = extension, - this._mime = mime - { - if (_extension != null && _extension[0] == '.') { - _extension = _extension.substring(1); - } - } + this._mime = mime; /// Get the mime type from this XType String get mime { diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index d98a8921df75..59098d2e853e 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -35,10 +35,15 @@ class FileSelectorPlugin extends FileSelectorPlatform { List allExtensions = List(); for (XTypeGroup group in acceptedTypes ?? []) { for (XType type in group.fileTypes ?? []) { - if (type.extension == null) { - continue; + if (type.extension != null) { + String extension = type.extension; + if (extension[0] != '.') { + extension = '.' + extension; + } + allExtensions.add(extension); + } else if (type.mime != null) { + allExtensions.add(type.mime); } - allExtensions.add('.' + type.extension); } } return allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; From 1eec76aa5e8d76fef9229fc751ff9c6d9f616a81 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 26 Aug 2020 16:47:52 -0700 Subject: [PATCH 047/151] Update XTypeGroup to new API --- .../file_selector/example/lib/main.dart | 14 ++--- .../file_selector/lib/file_selector.dart | 2 +- .../lib/src/types/types.dart | 2 +- .../lib/src/types/x_file/base.dart | 12 +++- .../lib/src/types/x_file/html.dart | 24 +++----- .../lib/src/types/x_file/io.dart | 52 ++++++++++------ .../lib/src/types/x_type/x_type.dart | 61 ------------------- .../src/types/x_type_group/x_type_group.dart | 53 ++++++++++++++++ .../lib/file_selector_web.dart | 33 ++++++---- .../test/test_driver/web_e2e.dart | 20 +++--- 10 files changed, 150 insertions(+), 123 deletions(-) delete mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart create mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index f658e256ec34..0c79faa72fc9 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -55,12 +55,11 @@ class _SaveTestState extends State { data = Uint8List.fromList(_fileController.text.codeUnits); XFile new_file; - XType type = XType(extension: '.txt', mime: 'plain/text'); if (_nameController.text == '') { - new_file = XFile.fromData(data, type: type); + new_file = XFile.fromData(data, mimeType: 'text/plain'); } else { - new_file = XFile.fromData(data, type: type, name: _nameController.text); + new_file = XFile.fromData(data, mimeType: 'text/plain', name: _nameController.text); } new_file.saveTo(path); @@ -128,9 +127,8 @@ class _LoadTestState extends State { final TextEditingController _extensionController = TextEditingController(); void _onLoadImageFile() async { - XType jpg = XType(extension: '.jpg'); - XType png = XType(extension: '.png'); - XTypeGroup typeGroup = XTypeGroup(label: 'images', fileTypes: [ jpg, png ]); + + XTypeGroup typeGroup = XTypeGroup(label: 'images', extensions: [ 'jpg', 'png' ]); XFile file = await loadFile(acceptedTypeGroups: [ typeGroup ]); @@ -145,9 +143,7 @@ class _LoadTestState extends State { } void _onLoadTextFile() async { - XType txt = XType(extension: '.txt'); - XType json = XType(extension: '.json'); - XTypeGroup typeGroup = XTypeGroup(label: 'images', fileTypes: [ txt, json ]); + XTypeGroup typeGroup = XTypeGroup(label: 'images', extensions: [ 'txt', 'json' ]); XFile file = await loadFile(acceptedTypeGroups: [typeGroup]); diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index 1da903db6ee1..d533373e2855 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -8,7 +8,7 @@ import 'dart:typed_data'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; export 'package:file_selector_platform_interface/file_selector_platform_interface.dart' - show XFile, XTypeGroup, XType; + show XFile, XTypeGroup; /// NEW API diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart index cae9d41efedf..8848c6751ba3 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart @@ -1,3 +1,3 @@ export 'x_file/x_file.dart'; -export 'x_type/x_type.dart'; +export 'x_type_group/x_type_group.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart index 1b5c81f9bde0..be9db430e924 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart @@ -1,6 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; -import '../x_type/x_type.dart'; +import '../x_type_group/x_type_group.dart'; /// The interface for a XFile. /// @@ -39,6 +39,11 @@ abstract class XFileBase { throw UnimplementedError('.name has not been implemented.'); } + /// For web, it may be necessary for a file to know its MIME type. + String get mimeType { + throw UnimplementedError('.mimeType has not been implemented.'); + } + /// Get the length of the file. Returns a `Future` that completes with the length in bytes. Future length() { throw UnimplementedError('.length() has not been implemented.'); @@ -70,4 +75,9 @@ abstract class XFileBase { Stream openRead([int start, int end]) { throw UnimplementedError('openRead() has not been implemented.'); } + + /// Get the last-modified time for the XFile + Future lastModified() { + throw UnimplementedError('openRead() has not been implemented.'); + } } \ No newline at end of file diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index f06333a185dc..7a3cde9ca7fb 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -7,19 +7,17 @@ import 'dart:html'; import './base.dart'; -import '../x_type/x_type.dart'; - /// A XFile that works on web. /// /// It wraps the bytes of a selected file. class XFile extends XFileBase { String path; - final XType type; + final String mimeType; final Uint8List _data; final int _length; final String name; - final DateTime lastModified; + final DateTime _lastModified; Element _target; final XFileTestOverrides _overrides; @@ -35,34 +33,36 @@ class XFile extends XFileBase { /// access to it while we create the ObjectUrl. XFile( this.path, { - this.type, + this.mimeType, this.name, int length, Uint8List bytes, - this.lastModified, + DateTime lastModified, @visibleForTesting XFileTestOverrides overrides, }) : _data = bytes, _length = length, _overrides = overrides, + _lastModified = lastModified, super(path); /// Construct an XFile from its data XFile.fromData( Uint8List bytes, { - this.type, + this.mimeType, this.name, int length, - this.lastModified, + DateTime lastModified, @visibleForTesting XFileTestOverrides overrides, }) : _data = bytes, _length = length, _overrides = overrides, + _lastModified = lastModified, super('') { Blob blob; - if (type.mime == null) { + if (mimeType == null) { blob = Blob([bytes]); } else { - blob = Blob([bytes], type.mime); + blob = Blob([bytes], mimeType); } this.path = Url.createObjectUrl(blob); } @@ -124,10 +124,6 @@ class XFile extends XFileBase { element.download = suggestedName; } - if (type.extension != null) { - element.download += '.' + type.extension; - } - return element; } diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart index dbb0706ab932..536b4456eca2 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart @@ -9,39 +9,55 @@ import '../types.dart'; /// A XFile backed by a dart:io File. class XFile extends XFileBase { final File _file; + final String mimeType; + final DateTime _lastModified; int _length; - final Uint8List _data; - - final XType type; - - DateTime get lastModified => _file.lastModifiedSync(); + final Uint8List _bytes; /// Construct a XFile object backed by a dart:io File. - XFile(String path, { this.type }) + XFile( + String path, { + this.mimeType, + String name, + int length, + Uint8List bytes, + DateTime lastModified, + }) : _file = File(path), - _data = null, + _bytes = null, + _lastModified = lastModified, super(path); /// Construct an XFile from its data - XFile.fromData(Uint8List data, { - this.type, + XFile.fromData(Uint8List bytes, { + this.mimeType, String path, String name, int length, - }): _data = data, + DateTime lastModified, + }): _bytes = bytes, _file = File(path), _length = length, + _lastModified = lastModified, super(path) { if (length == null) { - _length = data.length; + _length = bytes.length; + } + } + + @override + Future lastModified() { + if (_lastModified != null) { + return Future.value(_lastModified); } + return _file.lastModified(); } @override void saveTo(String path) async { File fileToSave = File(path); - await fileToSave.writeAsBytes(_data); + await fileToSave.writeAsBytes(_bytes); await fileToSave.create(); } @@ -65,24 +81,24 @@ class XFile extends XFileBase { @override Future readAsString({Encoding encoding = utf8}) { - if (_data != null) { - return Future.value(String.fromCharCodes(_data)); + if (_bytes != null) { + return Future.value(String.fromCharCodes(_bytes)); } return _file.readAsString(encoding: encoding); } @override Future readAsBytes() { - if (_data != null) { - return Future.value(_data); + if (_bytes != null) { + return Future.value(_bytes); } return _file.readAsBytes(); } @override Stream openRead([int start, int end]) async* { - if (_data != null) { - final bytes = _data; + if (_bytes != null) { + final bytes = _bytes; yield bytes.sublist(start ?? 0, end ?? bytes.length); } yield* _file diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart deleted file mode 100644 index ace2f602d45b..000000000000 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// TODO: should we be using this package or just extracting the important conversion data from it? -import 'package:mime_type/mime_type.dart'; - -/// A set of allowed XTypes -class XTypeGroup { - /// Creates a new group with the given label and file extensions. - const XTypeGroup({this.label, this.fileTypes}); - - /// The label for the grouping. On platforms that support selectable groups, - /// this will be visible to the user for selecting the group. - final String label; - - /// A list of allowed file extensions. E.g., ['png', 'jpg', 'jpeg', 'gif']. - /// - /// A null or empty list indicates any type is allowed. - final List fileTypes; -} - -/// A cross platform file type -class XType { - /// Variables to store type - String _mime; - String _extension; - - /// Default constructor - XType({ - String extension, - String mime, - }) : this._extension = extension, - this._mime = mime; - - /// Get the mime type from this XType - String get mime { - if (_mime == null) { - _mime = mimeFromExtension(_extension); - } - return _mime; - } - - /// Get the extension from this XType - String get extension { - if (_extension == null) { - _extension = extensionFromMime(_mime); - } - return _extension; - } -} \ No newline at end of file diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart new file mode 100644 index 000000000000..b38d3422ba92 --- /dev/null +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart @@ -0,0 +1,53 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// TODO: should we be using this package or just extracting the important conversion data from it? +import 'package:mime_type/mime_type.dart'; + +/// A set of allowed XTypes +class XTypeGroup { + /// Creates a new group with the given label and file extensions. + XTypeGroup({ + this.label, + this.extensions, + this.mimeTypes, + this.macUTIs, + this.webWildCards, + }) { + if ( + this.extensions == null && + this.mimeTypes == null && + this.macUTIs == null && + this.webWildCards == null + ) { + throw ArgumentError("At least one type must be provided for an XTypeGroup."); + } + } + + /// The 'name' or reference to this group of types + final String label; + + /// The extensions for this group + final List extensions; + + /// The MIME types for this group + final List mimeTypes; + + /// The UTIs for this group + final List macUTIs; + + /// The web wild cards for this group (ex: image/*, video/*) + final List webWildCards; +} + diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 59098d2e853e..8c10ae6aa05d 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -29,24 +29,35 @@ class FileSelectorPlugin extends FileSelectorPlatform { static void registerWith(Registrar registrar) { FileSelectorPlatform.instance = FileSelectorPlugin(); } + + void _verifyXTypeGroup(XTypeGroup group) { + if (group.extensions == null && group.mimeTypes == null && group.webWildCards == null) { + StateError("This XTypeGroup does not have types supported by the web implementation of loadFile."); + } + } /// Convert list of filter groups to a comma-separated string String _getStringFromFilterGroup (List acceptedTypes) { - List allExtensions = List(); + List allTypes = List(); for (XTypeGroup group in acceptedTypes ?? []) { - for (XType type in group.fileTypes ?? []) { - if (type.extension != null) { - String extension = type.extension; - if (extension[0] != '.') { - extension = '.' + extension; - } - allExtensions.add(extension); - } else if (type.mime != null) { - allExtensions.add(type.mime); + _verifyXTypeGroup(group); + + for (String mimeType in group.mimeTypes ?? []) { + allTypes.add(mimeType); + } + for (String extension in group.extensions ?? []) { + String ext = extension; + if (ext.isNotEmpty && [0] != '.') { + ext = '.' + ext; } + + allTypes.add(ext); + } + for (String webWildCard in group.webWildCards ?? []) { + allTypes.add(webWildCard); } } - return allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; + return allTypes?.where((e) => e.isNotEmpty)?.join(',') ?? ''; } /// Creates a file input element with only the accept attribute diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index 89bb1d0904f1..39a1852ed55d 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -19,11 +19,16 @@ import 'dart:developer'; final String domElementId = '__file_selector_web_file_input'; final String xFileDomElementId = '__x_file_dom_element'; -final textGroup = XTypeGroup(label: 'test', - fileTypes: [ - XType(extension: 'json', mime: 'application/json'), - XType(extension: 'txt', mime: 'text/plain'), - ]); +final textGroup = XTypeGroup( + label: 'Text Files', + extensions: ['txt', 'json'], + mimeTypes: ['plain/text', 'application/json'], +); + +final badGroup = XTypeGroup( + label: 'Non-Web Group', + macUTIs: ['fake-uti'], +); final String expectedStringContents = 'Hello, world!'; final Uint8List bytes = utf8.encode(expectedStringContents); @@ -68,7 +73,7 @@ void main() { expect(result, isNotNull); expect(result.getAttribute('type'), 'file'); - expect(result.getAttribute('accept'), '.json,.txt'); + expect(result.getAttribute('accept'), 'plain/text,application/json,.txt,.json'); }); testWidgets('input element is clicked', (WidgetTester tester) async { @@ -119,6 +124,7 @@ void main() { expect(loadedFile.name, textFile.name); }); }); + group('loadFiles(..)', () { @@ -141,7 +147,7 @@ void main() { expect(result, isNotNull); expect(result.getAttribute('type'), 'file'); - expect(result.getAttribute('accept'), '.json,.txt'); + expect(result.getAttribute('accept'), 'plain/text,application/json,.txt,.json'); expect(result.multiple, true); }); From b603098c99586bef7146fb400625c26d7bf3deaa Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 26 Aug 2020 17:04:35 -0700 Subject: [PATCH 048/151] Add confirmButtonText to all functions, acceptedTypeGroups to getSavePath Also mild refactor: move loadFile, loadFiles, getSavePath to top of file_selector_web.dart Update platform interface tests from picker to selector Fix web implementation tests to include mime types in expected value for file input element --- .../file_selector/lib/file_selector.dart | 10 ++- .../test/file_selector_test.dart | 16 +++-- .../method_channel_file_selector.dart | 5 ++ .../file_selector_interface.dart | 4 ++ .../file_picker_platform_interface_test.dart | 32 ++++----- .../lib/file_selector_web.dart | 70 ++++++++++--------- 6 files changed, 80 insertions(+), 57 deletions(-) diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index d533373e2855..788cf45fc47e 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -16,22 +16,26 @@ export 'package:file_selector_platform_interface/file_selector_platform_interfac Future loadFile({ List acceptedTypeGroups, String initialDirectory, + String confirmButtonText, }) { - return FileSelectorPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory); + return FileSelectorPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory, confirmButtonText: confirmButtonText); } /// Open file dialog for loading files and return a list of file paths Future> loadFiles({ List acceptedTypeGroups, String initialDirectory, + String confirmButtonText, }) { - return FileSelectorPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory); + return FileSelectorPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory, confirmButtonText: confirmButtonText); } /// Saves File to user's file system Future getSavePath({ + List acceptedTypeGroups, String initialDirectory, String suggestedName, + String confirmButtonText, }) async { - return FileSelectorPlatform.instance.getSavePath(initialDirectory: initialDirectory, suggestedName: suggestedName); + return FileSelectorPlatform.instance.getSavePath(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory, suggestedName: suggestedName, confirmButtonText: confirmButtonText); } \ No newline at end of file diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index 85cf78cbe776..837153e2e5f2 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -17,14 +17,18 @@ void main() { test('getSavePath', () async { String expectedPath = '/example/path'; + XTypeGroup typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); + when( mock.getSavePath( + acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', suggestedName: 'name', + confirmButtonText: 'save', ) ).thenAnswer((_) => Future.value(expectedPath)); - String result = await getSavePath(initialDirectory: 'dir', suggestedName: 'name'); + String result = await getSavePath(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', suggestedName: 'name', confirmButtonText: 'save'); expect(result, expectedPath); }); @@ -32,16 +36,17 @@ void main() { test('loadFile', () async { XFile file = XFile('path'); - XTypeGroup typeGroup = XTypeGroup(label: 'group', fileTypes: [ XType(extension: '.json', mime: 'application/json') ]); + XTypeGroup typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); when( mock.loadFile( acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', + confirmButtonText: 'load', ) ).thenAnswer((_) => Future.value(file)); - XFile result = await loadFile(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir'); + XFile result = await loadFile(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', confirmButtonText: 'load'); expect(result, isNotNull); }); @@ -49,16 +54,17 @@ void main() { test('loadFiles', () async { XFile file = XFile('path'); - XTypeGroup typeGroup = XTypeGroup(label: 'group', fileTypes: [ XType(extension: '.json', mime: 'application/json') ]); + XTypeGroup typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); when( mock.loadFiles( acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', + confirmButtonText: 'load', ) ).thenAnswer((_) => Future.value([file])); - List result = await loadFiles(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir'); + List result = await loadFiles(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', confirmButtonText: 'load'); expect(result, isNotNull); }); diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index 91ab40c9f840..fd4931b2e908 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -18,6 +18,7 @@ class MethodChannelFileSelector extends FileSelectorPlatform { Future loadFile({ List acceptedTypeGroups, String initialDirectory, + String confirmButtonText, }) { return _channel.invokeMethod( 'loadFile', @@ -33,6 +34,7 @@ class MethodChannelFileSelector extends FileSelectorPlatform { Future> loadFiles({ List acceptedTypeGroups, String initialDirectory, + String confirmButtonText, }) { return _channel.invokeMethod>( 'loadFiles', @@ -46,14 +48,17 @@ class MethodChannelFileSelector extends FileSelectorPlatform { /// Saves the file to user's Disk @override Future getSavePath({ + List acceptedTypeGroups, String initialDirectory, String suggestedName, + String confirmButtonText, }) async { return _channel.invokeMethod( 'saveFile', { 'initialDirectory': initialDirectory, 'suggestedName': suggestedName, + }, ); } diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index 94191fea7a0b..94213f5ba944 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -44,6 +44,7 @@ abstract class FileSelectorPlatform extends PlatformInterface { Future loadFile({ List acceptedTypeGroups, String initialDirectory, + String confirmButtonText, }) { throw UnimplementedError('loadFile() has not been implemented.'); } @@ -52,14 +53,17 @@ abstract class FileSelectorPlatform extends PlatformInterface { Future> loadFiles({ List acceptedTypeGroups, String initialDirectory, + String confirmButtonText, }) { throw UnimplementedError('loadFile() has not been implemented.'); } /// Open file dialog for saving files and return a file path at which to save Future getSavePath({ + List acceptedTypeGroups, String initialDirectory, String suggestedName, + String confirmButtonText, }) { throw UnimplementedError('saveFile() has not been implemented.'); } diff --git a/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart b/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart index e454ac6ca951..a1859fe054aa 100644 --- a/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart @@ -7,41 +7,39 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:file_picker_platform_interface/file_selector_platform_interface.dart'; -import 'package:file_picker_platform_interface/src/method_channel/method_channel_file_selector.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:file_selector_platform_interface/src/method_channel/method_channel_file_selector.dart'; void main() { - group('$FilePickerPlatform', () { - test('$MethodChannelFilePicker() is the default instance', () { - expect(FilePickerPlatform.instance, - isInstanceOf()); + group('$FileSelectorPlatform', () { + test('$MethodChannelFileSelector() is the default instance', () { + expect(FileSelectorPlatform.instance, + isInstanceOf()); }); test('Cannot be implemented with `implements`', () { expect(() { - FilePickerPlatform.instance = ImplementsFilePickerPlatform(); + FileSelectorPlatform.instance = ImplementsFileSelectorPlatform(); }, throwsA(isInstanceOf())); }); test('Can be mocked with `implements`', () { - final FilePickerPlatformMock mock = FilePickerPlatformMock(); - FilePickerPlatform.instance = mock; + final FileSelectorPlatformMock mock = FileSelectorPlatformMock(); + FileSelectorPlatform.instance = mock; }); test('Can be extended', () { - FilePickerPlatform.instance = ExtendsFilePickerPlatform(); + FileSelectorPlatform.instance = ExtendsFileSelectorPlatform(); }); }); - - } -class FilePickerPlatformMock extends Mock +class FileSelectorPlatformMock extends Mock with MockPlatformInterfaceMixin - implements FilePickerPlatform {} + implements FileSelectorPlatform {} -class ImplementsFilePickerPlatform extends Mock - implements FilePickerPlatform {} +class ImplementsFileSelectorPlatform extends Mock + implements FileSelectorPlatform {} -class ExtendsFilePickerPlatform extends FilePickerPlatform {} \ No newline at end of file +class ExtendsFileSelectorPlatform extends FileSelectorPlatform {} \ No newline at end of file diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 8c10ae6aa05d..30b3599d2551 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -12,6 +12,44 @@ final String _kFileSelectorInputsDomId = '__file_selector_web_file_input'; /// /// This class implements the `package:file_selector` functionality for the web. class FileSelectorPlugin extends FileSelectorPlatform { + + /// Open file dialog for loading files and return a XFile + @override + Future loadFile({ + List acceptedTypeGroups, + String initialDirectory, + String confirmButtonText, + }) { + Completer _completer = Completer(); + _loadFileHelper(false, acceptedTypeGroups).then((list) { + _completer.complete(list.first); + }) + .catchError((err) { + _completer.completeError(err); + }); + + return _completer.future; + } + + /// Open file dialog for loading files and return a XFile + @override + Future> loadFiles({ + List acceptedTypeGroups, + String initialDirectory, + String confirmButtonText, + }) { + return _loadFileHelper(true, acceptedTypeGroups); + } + + @override + Future getSavePath({ + List acceptedTypeGroups, + String initialDirectory, + String suggestedName, + String confirmButtonText, + }) => Future.value(); + + Element _target; final FileSelectorPluginTestOverrides _overrides; bool get _hasTestOverrides => _overrides != null; @@ -166,38 +204,6 @@ class FileSelectorPlugin extends FileSelectorPlatform { return getFilesWhenReady(element); } - - /// Open file dialog for loading files and return a XFile - @override - Future loadFile({ - List acceptedTypeGroups, - String initialDirectory, - }) { - Completer _completer = Completer(); - _loadFileHelper(false, acceptedTypeGroups).then((list) { - _completer.complete(list.first); - }) - .catchError((err) { - _completer.completeError(err); - }); - - return _completer.future; - } - - /// Open file dialog for loading files and return a XFile - @override - Future> loadFiles({ - List acceptedTypeGroups, - String initialDirectory, - }) { - return _loadFileHelper(true, acceptedTypeGroups); - } - - @override - Future getSavePath({ - String initialDirectory, - String suggestedName, - }) => Future.value(); } /// Overrides some functions to allow testing From 014823f5414174deb622a3d5212875a071383629 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 13:27:55 -0700 Subject: [PATCH 049/151] Fix `flutter analyze` issues --- .../file_selector/example/lib/main.dart | 23 ++++++----------- .../example/test/widget_test.dart | 5 ---- .../file_selector/lib/file_selector.dart | 3 --- .../test/file_selector_test.dart | 1 - .../method_channel_file_selector.dart | 2 -- .../file_selector_interface.dart | 1 - .../lib/src/types/x_file/base.dart | 2 +- .../lib/src/types/x_file/html.dart | 25 +++++++++++++------ .../lib/src/types/x_file/io.dart | 15 ++++++----- .../src/types/x_type_group/x_type_group.dart | 1 - .../file_picker_platform_interface_test.dart | 1 - .../lib/file_selector_web.dart | 5 ++-- .../test/test_driver/web_e2e.dart | 22 ++++++---------- 13 files changed, 42 insertions(+), 64 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 0c79faa72fc9..ffb85fa8e7fa 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -19,8 +19,8 @@ class MyApp extends StatelessWidget { ), home: MyHomePage(title: 'File Selector Demo Home Page'), routes: { - '/save' : (context) => SaveTest(title: "Save Example"), - '/load' : (context) => LoadTest(title: "Load Example"), + '/save' : (context) => SaveTest(), + '/load' : (context) => LoadTest(), }, ); } @@ -28,10 +28,8 @@ class MyApp extends StatelessWidget { /// Page for showing an example of saving with file_selector class SaveTest extends StatefulWidget { - SaveTest({Key key, this.title}) : super(key: key); - - /// Title of Home Page - final String title; + /// Constructor for the SaveTest page + SaveTest({Key key}) : super(key: key); @override _SaveTestState createState() => _SaveTestState(); @@ -40,7 +38,6 @@ class SaveTest extends StatefulWidget { class _SaveTestState extends State { final TextEditingController _fileController = TextEditingController(); final TextEditingController _nameController = TextEditingController(); - final TextEditingController _extensionController = TextEditingController(); @override void dispose() { @@ -70,7 +67,7 @@ class _SaveTestState extends State { return Scaffold( appBar: AppBar( - title: Text(widget.title), + title: Text("Save Example"), ), body: Center( child: Column( @@ -113,19 +110,13 @@ class _SaveTestState extends State { /// Screen that shows an example of loadFile(s) class LoadTest extends StatefulWidget { /// Default constructor - LoadTest({Key key, this.title}) : super(key: key); - - /// Title of Home Page - final String title; - + LoadTest({Key key}) : super(key: key); @override _LoadTestState createState() => _LoadTestState(); } class _LoadTestState extends State { - final TextEditingController _extensionController = TextEditingController(); - void _onLoadImageFile() async { XTypeGroup typeGroup = XTypeGroup(label: 'images', extensions: [ 'jpg', 'png' ]); @@ -159,7 +150,7 @@ class _LoadTestState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text(widget.title), + title: Text("Load Example"), ), body: Center( child: Column( diff --git a/packages/file_selector/file_selector/example/test/widget_test.dart b/packages/file_selector/file_selector/example/test/widget_test.dart index c9a60e1f271f..baa589079551 100644 --- a/packages/file_selector/file_selector/example/test/widget_test.dart +++ b/packages/file_selector/file_selector/example/test/widget_test.dart @@ -5,11 +5,6 @@ // 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() { } diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index 788cf45fc47e..fc770c707422 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -3,15 +3,12 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:typed_data'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; export 'package:file_selector_platform_interface/file_selector_platform_interface.dart' show XFile, XTypeGroup; -/// NEW API - /// Open file dialog for loading files and return a file path Future loadFile({ List acceptedTypeGroups, diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index 837153e2e5f2..88015a46d72b 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -4,7 +4,6 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter/material.dart'; import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:file_selector/file_selector.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index fd4931b2e908..5b94c3c0892a 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:typed_data'; - import 'package:flutter/services.dart'; import '../platform_interface/file_selector_interface.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index 94213f5ba944..2e7d954d0860 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:typed_data'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart index be9db430e924..d80677a3fd10 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart @@ -1,6 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; -import '../x_type_group/x_type_group.dart'; + /// The interface for a XFile. /// diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index 7a3cde9ca7fb..6768fde43b9e 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -52,22 +52,31 @@ class XFile extends XFileBase { this.name, int length, DateTime lastModified, + this.path, @visibleForTesting XFileTestOverrides overrides, }) : _data = bytes, _length = length, _overrides = overrides, _lastModified = lastModified, - super('') { - Blob blob; - if (mimeType == null) { - blob = Blob([bytes]); - } else { - blob = Blob([bytes], mimeType); + super(path) { + if (path == null) { + Blob blob; + if (mimeType == null) { + blob = Blob([bytes]); + } else { + blob = Blob([bytes], mimeType); + } + this.path = Url.createObjectUrl(blob); } - this.path = Url.createObjectUrl(blob); } - + @override + Future lastModified() async { + if (_lastModified != null) { + return Future.value(_lastModified); + } + return null; + } Future get _bytes async { if (_data != null) { diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart index 536b4456eca2..a8e1b50f7d9d 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart @@ -4,8 +4,6 @@ import 'dart:typed_data'; import './base.dart'; -import '../types.dart'; - /// A XFile backed by a dart:io File. class XFile extends XFileBase { final File _file; @@ -30,12 +28,13 @@ class XFile extends XFileBase { super(path); /// Construct an XFile from its data - XFile.fromData(Uint8List bytes, { - this.mimeType, - String path, - String name, - int length, - DateTime lastModified, + XFile.fromData( + Uint8List bytes, { + this.mimeType, + String path, + String name, + int length, + DateTime lastModified, }): _bytes = bytes, _file = File(path), _length = length, diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart index b38d3422ba92..5c06302cd365 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart @@ -13,7 +13,6 @@ // limitations under the License. // TODO: should we be using this package or just extracting the important conversion data from it? -import 'package:mime_type/mime_type.dart'; /// A set of allowed XTypes class XTypeGroup { diff --git a/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart b/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart index a1859fe054aa..6b1367143c99 100644 --- a/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'package:mockito/mockito.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 30b3599d2551..804d485bd896 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:html'; -import 'dart:typed_data'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; @@ -85,7 +84,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { } for (String extension in group.extensions ?? []) { String ext = extension; - if (ext.isNotEmpty && [0] != '.') { + if (ext.isNotEmpty && ext[0] != '.') { ext = '.' + ext; } @@ -133,7 +132,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { int length = file.size; DateTime modified = file.lastModifiedDate.add(timeZoneOffset); - xFiles.add(XFile(url, name: name, lastModified: modified)); + xFiles.add(XFile(url, name: name, lastModified: modified, length: length)); } return xFiles; diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index 39a1852ed55d..7317338cc466 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -2,8 +2,6 @@ import 'dart:async'; import 'package:e2e/e2e.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; - import 'dart:convert'; import 'dart:typed_data'; @@ -12,10 +10,6 @@ import 'dart:html'; import 'package:file_selector_web/file_selector_web.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; -import 'package:platform_detect/test_utils.dart' as platform; - -import 'dart:developer'; - final String domElementId = '__file_selector_web_file_input'; final String xFileDomElementId = '__x_file_dom_element'; @@ -46,14 +40,14 @@ void main() { FileSelectorPlugin plugin; - testWidgets('Create a DOM container', (WidgetTester tester) { - plugin = FileSelectorPlugin(); + group('loadFile(..)', () { + testWidgets('creates a DOM container', (WidgetTester tester) async { + plugin = FileSelectorPlugin(); - final result = querySelector('#${domElementId}'); - expect(result, isNotNull); - }); + final result = querySelector('#${domElementId}'); + expect(result, isNotNull); + }); - group('loadFile(..)', () { testWidgets('creates correct input element', (WidgetTester tester) async { final overrides = FileSelectorPluginTestOverrides( getFilesWhenReady: (_) => Future.value([ XFile('path') ]), @@ -91,7 +85,7 @@ void main() { bool clicked = false; mockInput.onClick.listen((event) => clicked = true); - final file = await plugin.loadFile(acceptedTypeGroups: [ textGroup ]); + await plugin.loadFile(acceptedTypeGroups: [ textGroup ]); expect(clicked, true); }); @@ -168,7 +162,7 @@ void main() { bool clicked = false; mockInput.onClick.listen((event) => clicked = true); - final file = await plugin.loadFiles(acceptedTypeGroups: [ textGroup ]); + await plugin.loadFiles(acceptedTypeGroups: [ textGroup ]); expect(clicked, true); }); From 7677ccdc34142976f4811b310d187c0139ab533f Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 13:28:20 -0700 Subject: [PATCH 050/151] Update unsupported.dart --- .../lib/src/types/x_file/unsupported.dart | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart index 8acdeb329b54..bf3d46d00e77 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart @@ -1,4 +1,5 @@ import 'dart:typed_data'; +import 'package:meta/meta.dart'; import './base.dart'; @@ -15,23 +16,38 @@ class XFile extends XFileBase { /// `path` of the file doesn't match what the user sees when selecting it /// (like in web) XFile( - String path, { - String name, - int length, - Uint8List bytes, - }) : super(path) { + String path, { + String mimeType, + String name, + int length, + Uint8List bytes, + DateTime lastModified, + @visibleForTesting XFileTestOverrides overrides, + }) : super(path) { throw UnimplementedError( 'XFile is not available in your current platform.'); } /// Construct a XFile object from its data XFile.fromData( - Uint8List data, { - String path, + Uint8List bytes, { + String mimeType, String name, int length, + DateTime lastModified, + String path, + @visibleForTesting XFileTestOverrides overrides, }) : super(path) { throw UnimplementedError( 'XFile is not available in your current platform.'); } } + +/// Overrides some functions of XFile for testing purposes +@visibleForTesting class XFileTestOverrides { + /// For overriding the creation of the file input element. + dynamic Function(String href, String suggestedName) createAnchorElement; + + /// Default constructor for overrides + XFileTestOverrides({this.createAnchorElement}); +} \ No newline at end of file From 4d30ca65ea333fa9062ba87bbcfd52245bd02d0b Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 13:35:04 -0700 Subject: [PATCH 051/151] Format code --- .../file_selector/example/lib/main.dart | 43 +++++------ .../example/test/widget_test.dart | 4 +- .../file_selector/lib/file_selector.dart | 20 +++-- .../test/file_selector_test.dart | 66 ++++++++-------- .../method_channel_file_selector.dart | 7 +- .../file_selector_interface.dart | 3 - .../lib/src/types/x_file/base.dart | 3 +- .../lib/src/types/x_file/html.dart | 38 +++++----- .../lib/src/types/x_file/io.dart | 27 ++++--- .../lib/src/types/x_file/unsupported.dart | 37 ++++----- .../lib/src/types/x_file/x_file.dart | 2 +- .../src/types/x_type_group/x_type_group.dart | 14 ++-- .../file_picker_platform_interface_test.dart | 3 +- .../lib/file_selector_web.dart | 70 +++++++++-------- .../file_selector_web/test/lib/main.dart | 1 - .../test/test_driver/web_e2e.dart | 75 ++++++++++--------- 16 files changed, 210 insertions(+), 203 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index ffb85fa8e7fa..06ae8125e4d6 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -19,8 +19,8 @@ class MyApp extends StatelessWidget { ), home: MyHomePage(title: 'File Selector Demo Home Page'), routes: { - '/save' : (context) => SaveTest(), - '/load' : (context) => LoadTest(), + '/save': (context) => SaveTest(), + '/load': (context) => LoadTest(), }, ); } @@ -56,7 +56,8 @@ class _SaveTestState extends State { if (_nameController.text == '') { new_file = XFile.fromData(data, mimeType: 'text/plain'); } else { - new_file = XFile.fromData(data, mimeType: 'text/plain', name: _nameController.text); + new_file = XFile.fromData(data, + mimeType: 'text/plain', name: _nameController.text); } new_file.saveTo(path); @@ -64,7 +65,6 @@ class _SaveTestState extends State { @override Widget build(BuildContext context) { - return Scaffold( appBar: AppBar( title: Text("Save Example"), @@ -98,7 +98,7 @@ class _SaveTestState extends State { SizedBox(height: 10), RaisedButton( child: Text('Press to save a text file'), - onPressed: () => { _saveFile() }, + onPressed: () => {_saveFile()}, ), ], ), @@ -118,23 +118,21 @@ class LoadTest extends StatefulWidget { class _LoadTestState extends State { void _onLoadImageFile() async { + XTypeGroup typeGroup = + XTypeGroup(label: 'images', extensions: ['jpg', 'png']); - XTypeGroup typeGroup = XTypeGroup(label: 'images', extensions: [ 'jpg', 'png' ]); - - XFile file = await loadFile(acceptedTypeGroups: [ typeGroup ]); + XFile file = await loadFile(acceptedTypeGroups: [typeGroup]); await showDialog( - context: context, - builder: (context) { - return ImageDisplay(file: file); - } - ); - - + context: context, + builder: (context) { + return ImageDisplay(file: file); + }); } void _onLoadTextFile() async { - XTypeGroup typeGroup = XTypeGroup(label: 'images', extensions: [ 'txt', 'json' ]); + XTypeGroup typeGroup = + XTypeGroup(label: 'images', extensions: ['txt', 'json']); XFile file = await loadFile(acceptedTypeGroups: [typeGroup]); @@ -142,8 +140,7 @@ class _LoadTestState extends State { context: context, builder: (context) { return TextDisplay(file: file); - } - ); + }); } @override @@ -204,7 +201,8 @@ class _TextDisplayState extends State { content: Scrollbar( child: SingleChildScrollView( child: Text( - fileContents ?? 'Loading file contents...\nThis may take a while if your file is large.', + fileContents ?? + 'Loading file contents...\nThis may take a while if your file is large.', ), ), ), @@ -233,7 +231,6 @@ class ImageDisplay extends StatefulWidget { } class _ImageDisplayState extends State { - @override void initState() { super.initState(); @@ -244,7 +241,6 @@ class _ImageDisplayState extends State { return AlertDialog( title: Text(widget.file.name), content: Image.network(widget.file.path), - actions: [ FlatButton( child: const Text('Close'), @@ -257,11 +253,8 @@ class _ImageDisplayState extends State { } } - - /// Home Page of the application class MyHomePage extends StatefulWidget { - /// Constructor for MyHomePage MyHomePage({Key key, this.title}) : super(key: key); @@ -274,7 +267,6 @@ class MyHomePage extends StatefulWidget { /// State of Home Page class _MyHomePageState extends State { - @override Widget build(BuildContext context) { return Scaffold( @@ -299,5 +291,4 @@ class _MyHomePageState extends State { ), ); } - } diff --git a/packages/file_selector/file_selector/example/test/widget_test.dart b/packages/file_selector/file_selector/example/test/widget_test.dart index baa589079551..570e0e4768dc 100644 --- a/packages/file_selector/file_selector/example/test/widget_test.dart +++ b/packages/file_selector/file_selector/example/test/widget_test.dart @@ -5,6 +5,4 @@ // 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. -void main() { - -} +void main() {} diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index fc770c707422..659b4e87fe4f 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -7,7 +7,7 @@ import 'dart:async'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; export 'package:file_selector_platform_interface/file_selector_platform_interface.dart' - show XFile, XTypeGroup; + show XFile, XTypeGroup; /// Open file dialog for loading files and return a file path Future loadFile({ @@ -15,7 +15,10 @@ Future loadFile({ String initialDirectory, String confirmButtonText, }) { - return FileSelectorPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory, confirmButtonText: confirmButtonText); + return FileSelectorPlatform.instance.loadFile( + acceptedTypeGroups: acceptedTypeGroups, + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText); } /// Open file dialog for loading files and return a list of file paths @@ -24,7 +27,10 @@ Future> loadFiles({ String initialDirectory, String confirmButtonText, }) { - return FileSelectorPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory, confirmButtonText: confirmButtonText); + return FileSelectorPlatform.instance.loadFiles( + acceptedTypeGroups: acceptedTypeGroups, + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText); } /// Saves File to user's file system @@ -34,5 +40,9 @@ Future getSavePath({ String suggestedName, String confirmButtonText, }) async { - return FileSelectorPlatform.instance.getSavePath(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory, suggestedName: suggestedName, confirmButtonText: confirmButtonText); -} \ No newline at end of file + return FileSelectorPlatform.instance.getSavePath( + acceptedTypeGroups: acceptedTypeGroups, + initialDirectory: initialDirectory, + suggestedName: suggestedName, + confirmButtonText: confirmButtonText); +} diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index 88015a46d72b..81b1772fb3bc 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; @@ -16,18 +15,21 @@ void main() { test('getSavePath', () async { String expectedPath = '/example/path'; - XTypeGroup typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); + XTypeGroup typeGroup = + XTypeGroup(label: 'group', extensions: ['jpg', 'png']); - when( - mock.getSavePath( - acceptedTypeGroups: [typeGroup], - initialDirectory: 'dir', - suggestedName: 'name', - confirmButtonText: 'save', - ) - ).thenAnswer((_) => Future.value(expectedPath)); + when(mock.getSavePath( + acceptedTypeGroups: [typeGroup], + initialDirectory: 'dir', + suggestedName: 'name', + confirmButtonText: 'save', + )).thenAnswer((_) => Future.value(expectedPath)); - String result = await getSavePath(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', suggestedName: 'name', confirmButtonText: 'save'); + String result = await getSavePath( + acceptedTypeGroups: [typeGroup], + initialDirectory: 'dir', + suggestedName: 'name', + confirmButtonText: 'save'); expect(result, expectedPath); }); @@ -35,17 +37,19 @@ void main() { test('loadFile', () async { XFile file = XFile('path'); - XTypeGroup typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); + XTypeGroup typeGroup = + XTypeGroup(label: 'group', extensions: ['jpg', 'png']); - when( - mock.loadFile( - acceptedTypeGroups: [typeGroup], - initialDirectory: 'dir', - confirmButtonText: 'load', - ) - ).thenAnswer((_) => Future.value(file)); + when(mock.loadFile( + acceptedTypeGroups: [typeGroup], + initialDirectory: 'dir', + confirmButtonText: 'load', + )).thenAnswer((_) => Future.value(file)); - XFile result = await loadFile(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', confirmButtonText: 'load'); + XFile result = await loadFile( + acceptedTypeGroups: [typeGroup], + initialDirectory: 'dir', + confirmButtonText: 'load'); expect(result, isNotNull); }); @@ -53,17 +57,19 @@ void main() { test('loadFiles', () async { XFile file = XFile('path'); - XTypeGroup typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); + XTypeGroup typeGroup = + XTypeGroup(label: 'group', extensions: ['jpg', 'png']); - when( - mock.loadFiles( - acceptedTypeGroups: [typeGroup], - initialDirectory: 'dir', - confirmButtonText: 'load', - ) - ).thenAnswer((_) => Future.value([file])); + when(mock.loadFiles( + acceptedTypeGroups: [typeGroup], + initialDirectory: 'dir', + confirmButtonText: 'load', + )).thenAnswer((_) => Future.value([file])); - List result = await loadFiles(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', confirmButtonText: 'load'); + List result = await loadFiles( + acceptedTypeGroups: [typeGroup], + initialDirectory: 'dir', + confirmButtonText: 'load'); expect(result, isNotNull); }); @@ -71,4 +77,4 @@ void main() { class MockFileSelector extends Mock with MockPlatformInterfaceMixin - implements FileSelectorPlatform {} \ No newline at end of file + implements FileSelectorPlatform {} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index 5b94c3c0892a..097075c96287 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -20,7 +20,7 @@ class MethodChannelFileSelector extends FileSelectorPlatform { }) { return _channel.invokeMethod( 'loadFile', - { + { 'acceptedTypes': acceptedTypeGroups, 'initialDirectory': initialDirectory, }, @@ -36,7 +36,7 @@ class MethodChannelFileSelector extends FileSelectorPlatform { }) { return _channel.invokeMethod>( 'loadFiles', - { + { 'acceptedTypes': acceptedTypeGroups, 'initialDirectory': initialDirectory, }, @@ -53,10 +53,9 @@ class MethodChannelFileSelector extends FileSelectorPlatform { }) async { return _channel.invokeMethod( 'saveFile', - { + { 'initialDirectory': initialDirectory, 'suggestedName': suggestedName, - }, ); } diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index 2e7d954d0860..66b14c75efd8 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -9,9 +9,6 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import '../method_channel/method_channel_file_selector.dart'; - - - /// The interface that implementations of file_picker must implement. /// /// Platform implementations should extend this class rather than implement it as `file_picker` diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart index d80677a3fd10..7ea050ff28db 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; - /// The interface for a XFile. /// /// A XFile is a container that wraps the path of a selected @@ -80,4 +79,4 @@ abstract class XFileBase { Future lastModified() { throw UnimplementedError('openRead() has not been implemented.'); } -} \ No newline at end of file +} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index 6768fde43b9e..fbbe0a139dc1 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -32,14 +32,14 @@ class XFile extends XFileBase { /// `name` needs to be passed from the outside, since we only have /// access to it while we create the ObjectUrl. XFile( - this.path, { - this.mimeType, - this.name, - int length, - Uint8List bytes, - DateTime lastModified, - @visibleForTesting XFileTestOverrides overrides, - }) : _data = bytes, + this.path, { + this.mimeType, + this.name, + int length, + Uint8List bytes, + DateTime lastModified, + @visibleForTesting XFileTestOverrides overrides, + }) : _data = bytes, _length = length, _overrides = overrides, _lastModified = lastModified, @@ -47,14 +47,14 @@ class XFile extends XFileBase { /// Construct an XFile from its data XFile.fromData( - Uint8List bytes, { - this.mimeType, - this.name, - int length, - DateTime lastModified, - this.path, - @visibleForTesting XFileTestOverrides overrides, - }) : _data = bytes, + Uint8List bytes, { + this.mimeType, + this.name, + int length, + DateTime lastModified, + this.path, + @visibleForTesting XFileTestOverrides overrides, + }) : _data = bytes, _length = length, _overrides = overrides, _lastModified = lastModified, @@ -148,8 +148,7 @@ class XFile extends XFileBase { Element _ensureInitialized(String id) { var target = querySelector('#${id}'); if (target == null) { - final Element targetElement = - Element.tag('flt-x-file')..id = id; + final Element targetElement = Element.tag('flt-x-file')..id = id; querySelector('body').children.add(targetElement); target = targetElement; @@ -158,7 +157,6 @@ class XFile extends XFileBase { } } - /// Overrides some functions to allow testing @visibleForTesting class XFileTestOverrides { @@ -167,4 +165,4 @@ class XFileTestOverrides { /// Default constructor for overrides XFileTestOverrides({this.createAnchorElement}); -} \ No newline at end of file +} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart index a8e1b50f7d9d..bd8470a1d8ca 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart @@ -16,26 +16,25 @@ class XFile extends XFileBase { /// Construct a XFile object backed by a dart:io File. XFile( String path, { - this.mimeType, - String name, - int length, - Uint8List bytes, - DateTime lastModified, - }) - : _file = File(path), + this.mimeType, + String name, + int length, + Uint8List bytes, + DateTime lastModified, + }) : _file = File(path), _bytes = null, _lastModified = lastModified, super(path); /// Construct an XFile from its data XFile.fromData( - Uint8List bytes, { - this.mimeType, - String path, - String name, - int length, - DateTime lastModified, - }): _bytes = bytes, + Uint8List bytes, { + this.mimeType, + String path, + String name, + int length, + DateTime lastModified, + }) : _bytes = bytes, _file = File(path), _length = length, _lastModified = lastModified, diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart index bf3d46d00e77..f5fe388e0899 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart @@ -16,38 +16,39 @@ class XFile extends XFileBase { /// `path` of the file doesn't match what the user sees when selecting it /// (like in web) XFile( - String path, { - String mimeType, - String name, - int length, - Uint8List bytes, - DateTime lastModified, - @visibleForTesting XFileTestOverrides overrides, - }) : super(path) { + String path, { + String mimeType, + String name, + int length, + Uint8List bytes, + DateTime lastModified, + @visibleForTesting XFileTestOverrides overrides, + }) : super(path) { throw UnimplementedError( 'XFile is not available in your current platform.'); } /// Construct a XFile object from its data XFile.fromData( - Uint8List bytes, { - String mimeType, - String name, - int length, - DateTime lastModified, - String path, - @visibleForTesting XFileTestOverrides overrides, - }) : super(path) { + Uint8List bytes, { + String mimeType, + String name, + int length, + DateTime lastModified, + String path, + @visibleForTesting XFileTestOverrides overrides, + }) : super(path) { throw UnimplementedError( 'XFile is not available in your current platform.'); } } /// Overrides some functions of XFile for testing purposes -@visibleForTesting class XFileTestOverrides { +@visibleForTesting +class XFileTestOverrides { /// For overriding the creation of the file input element. dynamic Function(String href, String suggestedName) createAnchorElement; /// Default constructor for overrides XFileTestOverrides({this.createAnchorElement}); -} \ No newline at end of file +} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart index 72d156cc58fd..f966a7c9a3aa 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart @@ -1,3 +1,3 @@ export 'unsupported.dart' if (dart.library.html) 'html.dart' - if (dart.library.io) 'io.dart'; \ No newline at end of file + if (dart.library.io) 'io.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart index 5c06302cd365..8b02af8ea24e 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart @@ -24,13 +24,12 @@ class XTypeGroup { this.macUTIs, this.webWildCards, }) { - if ( - this.extensions == null && - this.mimeTypes == null && - this.macUTIs == null && - this.webWildCards == null - ) { - throw ArgumentError("At least one type must be provided for an XTypeGroup."); + if (this.extensions == null && + this.mimeTypes == null && + this.macUTIs == null && + this.webWildCards == null) { + throw ArgumentError( + "At least one type must be provided for an XTypeGroup."); } } @@ -49,4 +48,3 @@ class XTypeGroup { /// The web wild cards for this group (ex: image/*, video/*) final List webWildCards; } - diff --git a/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart b/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart index 6b1367143c99..465900fa429a 100644 --- a/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart @@ -33,7 +33,6 @@ void main() { }); } - class FileSelectorPlatformMock extends Mock with MockPlatformInterfaceMixin implements FileSelectorPlatform {} @@ -41,4 +40,4 @@ class FileSelectorPlatformMock extends Mock class ImplementsFileSelectorPlatform extends Mock implements FileSelectorPlatform {} -class ExtendsFileSelectorPlatform extends FileSelectorPlatform {} \ No newline at end of file +class ExtendsFileSelectorPlatform extends FileSelectorPlatform {} diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 804d485bd896..02e5a1a2e79c 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -11,7 +11,6 @@ final String _kFileSelectorInputsDomId = '__file_selector_web_file_input'; /// /// This class implements the `package:file_selector` functionality for the web. class FileSelectorPlugin extends FileSelectorPlatform { - /// Open file dialog for loading files and return a XFile @override Future loadFile({ @@ -22,8 +21,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { Completer _completer = Completer(); _loadFileHelper(false, acceptedTypeGroups).then((list) { _completer.complete(list.first); - }) - .catchError((err) { + }).catchError((err) { _completer.completeError(err); }); @@ -46,14 +44,14 @@ class FileSelectorPlugin extends FileSelectorPlatform { String initialDirectory, String suggestedName, String confirmButtonText, - }) => Future.value(); - - + }) => + Future.value(); + Element _target; final FileSelectorPluginTestOverrides _overrides; bool get _hasTestOverrides => _overrides != null; - /// Default constructor, initializes _target to a DOM element that we can use + /// Default constructor, initializes _target to a DOM element that we can use /// to host HTML elements. /// overrides parameter allows for testing to override functions FileSelectorPlugin({ @@ -68,13 +66,16 @@ class FileSelectorPlugin extends FileSelectorPlatform { } void _verifyXTypeGroup(XTypeGroup group) { - if (group.extensions == null && group.mimeTypes == null && group.webWildCards == null) { - StateError("This XTypeGroup does not have types supported by the web implementation of loadFile."); + if (group.extensions == null && + group.mimeTypes == null && + group.webWildCards == null) { + StateError( + "This XTypeGroup does not have types supported by the web implementation of loadFile."); } } - + /// Convert list of filter groups to a comma-separated string - String _getStringFromFilterGroup (List acceptedTypes) { + String _getStringFromFilterGroup(List acceptedTypes) { List allTypes = List(); for (XTypeGroup group in acceptedTypes ?? []) { _verifyXTypeGroup(group); @@ -82,12 +83,12 @@ class FileSelectorPlugin extends FileSelectorPlatform { for (String mimeType in group.mimeTypes ?? []) { allTypes.add(mimeType); } - for (String extension in group.extensions ?? []) { + for (String extension in group.extensions ?? []) { String ext = extension; if (ext.isNotEmpty && ext[0] != '.') { ext = '.' + ext; } - + allTypes.add(ext); } for (String webWildCard in group.webWildCards ?? []) { @@ -99,11 +100,12 @@ class FileSelectorPlugin extends FileSelectorPlatform { /// Creates a file input element with only the accept attribute @visibleForTesting - FileUploadInputElement createFileInputElement(String accepted, bool multiple) { + FileUploadInputElement createFileInputElement( + String accepted, bool multiple) { if (_hasTestOverrides && _overrides.createFileInputElement != null) { return _overrides.createFileInputElement(accepted, multiple); } - + final FileUploadInputElement element = FileUploadInputElement(); if (accepted.isNotEmpty) { element.accept = accepted; @@ -121,9 +123,9 @@ class FileSelectorPlugin extends FileSelectorPlatform { element.click(); } - List _getXFilesFromFiles (List files) { + List _getXFilesFromFiles(List files) { List xFiles = List(); - + Duration timeZoneOffset = DateTime.now().timeZoneOffset; for (File file in files) { @@ -132,7 +134,8 @@ class FileSelectorPlugin extends FileSelectorPlatform { int length = file.size; DateTime modified = file.lastModifiedDate.add(timeZoneOffset); - xFiles.add(XFile(url, name: name, lastModified: modified, length: length)); + xFiles + .add(XFile(url, name: name, lastModified: modified, length: length)); } return xFiles; @@ -141,18 +144,18 @@ class FileSelectorPlugin extends FileSelectorPlatform { /// Getter for retrieving files from an input element @visibleForTesting List getFilesFromInputElement(InputElement element) { - if(_hasTestOverrides && _overrides.getFilesFromInputElement != null) { + if (_hasTestOverrides && _overrides.getFilesFromInputElement != null) { return _overrides.getFilesFromInputElement(element); } return element?.files ?? []; } - + /// Listen for file input element to change and retrieve files when /// this happens. @visibleForTesting - Future> getFilesWhenReady(InputElement element) { - if(_hasTestOverrides && _overrides.getFilesWhenReady != null) { + Future> getFilesWhenReady(InputElement element) { + if (_hasTestOverrides && _overrides.getFilesWhenReady != null) { return _overrides.getFilesWhenReady(element); } @@ -182,22 +185,24 @@ class FileSelectorPlugin extends FileSelectorPlatform { Element _ensureInitialized(String id) { var target = querySelector('#${id}'); if (target == null) { - final Element targetElement = - Element.tag('flt-file-picker-inputs')..id = id; + final Element targetElement = Element.tag('flt-file-picker-inputs') + ..id = id; querySelector('body').children.add(targetElement); target = targetElement; } return target; } - + /// NEW API - + /// Load Helper - Future> _loadFileHelper (bool multiple, List acceptedTypes) { - final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); + Future> _loadFileHelper( + bool multiple, List acceptedTypes) { + final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); - final FileUploadInputElement element = createFileInputElement(acceptedTypeString, multiple); + final FileUploadInputElement element = + createFileInputElement(acceptedTypeString, multiple); _addElementToDomAndClick(element); @@ -217,5 +222,8 @@ class FileSelectorPluginTestOverrides { /// For overriding waiting for the files to be ready. Useful for testing so we do not hang here. Future> Function(InputElement input) getFilesWhenReady; - FileSelectorPluginTestOverrides({this.createFileInputElement, this.getFilesFromInputElement, this.getFilesWhenReady}); -} \ No newline at end of file + FileSelectorPluginTestOverrides( + {this.createFileInputElement, + this.getFilesFromInputElement, + this.getFilesWhenReady}); +} diff --git a/packages/file_selector/file_selector_web/test/lib/main.dart b/packages/file_selector/file_selector_web/test/lib/main.dart index 4c55f6c95a7a..da6e56f8ea2b 100644 --- a/packages/file_selector/file_selector_web/test/lib/main.dart +++ b/packages/file_selector/file_selector_web/test/lib/main.dart @@ -19,4 +19,3 @@ class MyAppState extends State { return Text('Testing... Look at the console output for results!'); } } - diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index 7317338cc466..47515a55a53b 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -36,7 +36,8 @@ final File textFile2 = File([bytes2], 'test2.txt'); void main() { E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; - print("Note that the following message can be ignored in the test output:\n'File chooser dialog can only be shown with a user activation'"); + print( + "Note that the following message can be ignored in the test output:\n'File chooser dialog can only be shown with a user activation'"); FileSelectorPlugin plugin; @@ -50,7 +51,7 @@ void main() { testWidgets('creates correct input element', (WidgetTester tester) async { final overrides = FileSelectorPluginTestOverrides( - getFilesWhenReady: (_) => Future.value([ XFile('path') ]), + getFilesWhenReady: (_) => Future.value([XFile('path')]), ); plugin = FileSelectorPlugin( @@ -59,25 +60,28 @@ void main() { final container = querySelector('#${domElementId}'); - final file = await plugin.loadFile(acceptedTypeGroups: [ textGroup ]); + final file = await plugin.loadFile(acceptedTypeGroups: [textGroup]); expect(file, isNotNull); - final result = container?.children?.firstWhere((element) => element.tagName == 'INPUT', orElse: () => null); + final result = container?.children?.firstWhere( + (element) => element.tagName == 'INPUT', + orElse: () => null); expect(result, isNotNull); expect(result.getAttribute('type'), 'file'); - expect(result.getAttribute('accept'), 'plain/text,application/json,.txt,.json'); + expect(result.getAttribute('accept'), + 'plain/text,application/json,.txt,.json'); }); testWidgets('input element is clicked', (WidgetTester tester) async { final mockInput = FileUploadInputElement(); final overrides = FileSelectorPluginTestOverrides( - getFilesWhenReady: (_) => Future.value([ XFile('path') ]), + getFilesWhenReady: (_) => Future.value([XFile('path')]), createFileInputElement: (_, __) => mockInput, ); - + plugin = FileSelectorPlugin( overrides: overrides, ); @@ -85,7 +89,7 @@ void main() { bool clicked = false; mockInput.onClick.listen((event) => clicked = true); - await plugin.loadFile(acceptedTypeGroups: [ textGroup ]); + await plugin.loadFile(acceptedTypeGroups: [textGroup]); expect(clicked, true); }); @@ -119,12 +123,10 @@ void main() { }); }); - - group('loadFiles(..)', () { testWidgets('creates correct input element', (WidgetTester tester) async { final overrides = FileSelectorPluginTestOverrides( - getFilesWhenReady: (_) => Future.value([ XFile('path'), XFile('path2') ]), + getFilesWhenReady: (_) => Future.value([XFile('path'), XFile('path2')]), ); plugin = FileSelectorPlugin( @@ -133,15 +135,18 @@ void main() { final container = querySelector('#${domElementId}'); - final files = await plugin.loadFiles(acceptedTypeGroups: [ textGroup ]); + final files = await plugin.loadFiles(acceptedTypeGroups: [textGroup]); expect(files, isNotNull); - final FileUploadInputElement result = container?.children?.firstWhere((element) => element.tagName == 'INPUT', orElse: () => null); + final FileUploadInputElement result = container?.children?.firstWhere( + (element) => element.tagName == 'INPUT', + orElse: () => null); expect(result, isNotNull); expect(result.getAttribute('type'), 'file'); - expect(result.getAttribute('accept'), 'plain/text,application/json,.txt,.json'); + expect(result.getAttribute('accept'), + 'plain/text,application/json,.txt,.json'); expect(result.multiple, true); }); @@ -149,12 +154,10 @@ void main() { final mockInput = FileUploadInputElement(); final overrides = FileSelectorPluginTestOverrides( - getFilesWhenReady: (_) => Future.value([ XFile('path') ]), + getFilesWhenReady: (_) => Future.value([XFile('path')]), createFileInputElement: (_, __) => mockInput, ); - - plugin = FileSelectorPlugin( overrides: overrides, ); @@ -162,7 +165,7 @@ void main() { bool clicked = false; mockInput.onClick.listen((event) => clicked = true); - await plugin.loadFiles(acceptedTypeGroups: [ textGroup ]); + await plugin.loadFiles(acceptedTypeGroups: [textGroup]); expect(clicked, true); }); @@ -196,7 +199,7 @@ void main() { final contents = await loadedFile1.readAsString(); expect(contents, expectedStringContents); expect(loadedFile1.name, textFile.name); - + final contents2 = await loadedFile2.readAsString(); expect(contents2, expectedStringContents2); expect(loadedFile2.name, textFile2.name); @@ -212,45 +215,47 @@ void main() { group('XFile saveTo(..)', () { testWidgets('creates a DOM container', (WidgetTester tester) async { XFile file = XFile.fromData(bytes); - + await file.saveTo(''); final container = querySelector('#${xFileDomElementId}'); - + expect(container, isNotNull); }); - + testWidgets('create anchor element', (WidgetTester tester) async { XFile file = XFile.fromData(bytes, name: textFile.name); - + await file.saveTo('path'); final container = querySelector('#${xFileDomElementId}'); - final AnchorElement element = container?.children?.firstWhere((element) => element.tagName == 'A', orElse: () => null); - + final AnchorElement element = container?.children + ?.firstWhere((element) => element.tagName == 'A', orElse: () => null); + expect(element, isNotNull); expect(element.href, file.path); expect(element.download, file.name); }); - + testWidgets('anchor element is clicked', (WidgetTester tester) async { final mockAnchor = AnchorElement(); - + XFileTestOverrides overrides = XFileTestOverrides( - createAnchorElement: (_,__) => mockAnchor, + createAnchorElement: (_, __) => mockAnchor, ); - - XFile file = XFile.fromData(bytes, name: textFile.name, overrides: overrides); - + + XFile file = + XFile.fromData(bytes, name: textFile.name, overrides: overrides); + bool clicked = false; mockAnchor.onClick.listen((event) => clicked = true); - + await file.saveTo('path'); - + expect(clicked, true); }); }); - print("Note that the following message can be ignored in the test output:\n'File chooser dialog can only be shown with a user activation'"); - + print( + "Note that the following message can be ignored in the test output:\n'File chooser dialog can only be shown with a user activation'"); } From 861c1036ae53e301d43824b0f8ac901c8615b981 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 13:54:59 -0700 Subject: [PATCH 052/151] Document all public members --- .../file_selector/file_selector_web/lib/file_selector_web.dart | 1 + packages/file_selector/file_selector_web/test/lib/main.dart | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 02e5a1a2e79c..93edaa2f59c5 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -222,6 +222,7 @@ class FileSelectorPluginTestOverrides { /// For overriding waiting for the files to be ready. Useful for testing so we do not hang here. Future> Function(InputElement input) getFilesWhenReady; + /// Constructor for test override class FileSelectorPluginTestOverrides( {this.createFileInputElement, this.getFilesFromInputElement, diff --git a/packages/file_selector/file_selector_web/test/lib/main.dart b/packages/file_selector/file_selector_web/test/lib/main.dart index da6e56f8ea2b..84c77ea76f01 100644 --- a/packages/file_selector/file_selector_web/test/lib/main.dart +++ b/packages/file_selector/file_selector_web/test/lib/main.dart @@ -8,11 +8,13 @@ void main() { runApp(MyApp()); } +/// An app that runs the tests class MyApp extends StatefulWidget { @override MyAppState createState() => MyAppState(); } +/// State for MyApp class MyAppState extends State { @override Widget build(BuildContext context) { From c90794ea8c5d5e6a12971a2794bdf925fa20b4e2 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 15:47:52 -0700 Subject: [PATCH 053/151] Move helpers out of XFile class --- .../lib/src/types/x_file/html.dart | 49 +++------------ .../lib/src/web_helpers/web_helpers.dart | 34 ++++++++++ .../lib/file_selector_web.dart | 62 +++++++------------ 3 files changed, 64 insertions(+), 81 deletions(-) create mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/web_helpers/web_helpers.dart diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index fbbe0a139dc1..2493e9dd2a9d 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -5,6 +5,7 @@ import 'package:http/http.dart' as http show readBytes; import 'package:meta/meta.dart'; import 'dart:html'; +import '../../web_helpers/web_helpers.dart'; import './base.dart'; /// A XFile that works on web. @@ -110,50 +111,18 @@ class XFile extends XFileBase { /// For the web implementation, the path variable is ignored. void saveTo(String path) async { // Create a DOM container where we can host the anchor. - _target = _ensureInitialized('__x_file_dom_element'); + _target = ensureInitialized('__x_file_dom_element'); // Create an tag with the appropriate download attributes and click it - final AnchorElement element = createAnchorElement(this.path, this.name); + // May be overridden with XFileTestOverrides + final AnchorElement element = + (_hasTestOverrides && _overrides.createAnchorElement != null) + ? _overrides.createAnchorElement(this.path, this.name) + : createAnchorElement(this.path, this.name); - _addElementToDomAndClick(element); - } - - /// Create anchor element with download attribute - @visibleForTesting - AnchorElement createAnchorElement(String href, String suggestedName) { - if (_hasTestOverrides && _overrides.createAnchorElement != null) { - return _overrides.createAnchorElement(href, suggestedName); - } - - final element = AnchorElement(href: href); - - if (suggestedName == null) { - element.download = 'download'; - } else { - element.download = suggestedName; - } - - return element; - } - - void _addElementToDomAndClick(Element element) { - // Add the file input element and click it - // All previous elements will be removed before adding the new one + // Clear the children in our container so we can add an element to click _target.children.clear(); - _target.children.add(element); - element.click(); - } - - /// Initializes a DOM container where we can host elements. - Element _ensureInitialized(String id) { - var target = querySelector('#${id}'); - if (target == null) { - final Element targetElement = Element.tag('flt-x-file')..id = id; - - querySelector('body').children.add(targetElement); - target = targetElement; - } - return target; + addElementToContainerAndClick(_target, element); } } diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/web_helpers/web_helpers.dart b/packages/file_selector/file_selector_platform_interface/lib/src/web_helpers/web_helpers.dart new file mode 100644 index 000000000000..9e40e562bc9a --- /dev/null +++ b/packages/file_selector/file_selector_platform_interface/lib/src/web_helpers/web_helpers.dart @@ -0,0 +1,34 @@ +import 'dart:html'; + +/// Create anchor element with download attribute +AnchorElement createAnchorElement(String href, String suggestedName) { + final element = AnchorElement(href: href); + + if (suggestedName == null) { + element.download = 'download'; + } else { + element.download = suggestedName; + } + + return element; +} + +/// Add an element to a container and click it +void addElementToContainerAndClick(Element container, Element element) { + // Add the element and click it + // All previous elements will be removed before adding the new one + container.children.add(element); + element.click(); +} + +/// Initializes a DOM container where we can host elements. +Element ensureInitialized(String id) { + var target = querySelector('#${id}'); + if (target == null) { + final Element targetElement = Element.tag('flt-x-file')..id = id; + + querySelector('body').children.add(targetElement); + target = targetElement; + } + return target; +} diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 93edaa2f59c5..a378320b5e39 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:html'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:file_selector_platform_interface/src/web_helpers/web_helpers.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:meta/meta.dart'; @@ -11,6 +12,11 @@ final String _kFileSelectorInputsDomId = '__file_selector_web_file_input'; /// /// This class implements the `package:file_selector` functionality for the web. class FileSelectorPlugin extends FileSelectorPlatform { + Element _target; + final FileSelectorPluginTestOverrides _overrides; + + bool get _hasTestOverrides => _overrides != null; + /// Open file dialog for loading files and return a XFile @override Future loadFile({ @@ -47,9 +53,19 @@ class FileSelectorPlugin extends FileSelectorPlatform { }) => Future.value(); - Element _target; - final FileSelectorPluginTestOverrides _overrides; - bool get _hasTestOverrides => _overrides != null; + /// Load Helper + Future> _loadFileHelper( + bool multiple, List acceptedTypes) { + final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); + + final FileUploadInputElement element = + createFileInputElement(acceptedTypeString, multiple); + + _target.children.clear(); + addElementToContainerAndClick(_target, element); + + return getFilesWhenReady(element); + } /// Default constructor, initializes _target to a DOM element that we can use /// to host HTML elements. @@ -57,7 +73,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { FileSelectorPlugin({ @visibleForTesting FileSelectorPluginTestOverrides overrides, }) : _overrides = overrides { - _target = _ensureInitialized(_kFileSelectorInputsDomId); + _target = ensureInitialized(_kFileSelectorInputsDomId); } /// Registers this class as the default instance of [FileSelectorPlatform]. @@ -74,7 +90,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { } } - /// Convert list of filter groups to a comma-separated string + /// Convert list of XTypeGroups to a comma-separated string String _getStringFromFilterGroup(List acceptedTypes) { List allTypes = List(); for (XTypeGroup group in acceptedTypes ?? []) { @@ -115,14 +131,6 @@ class FileSelectorPlugin extends FileSelectorPlatform { return element; } - void _addElementToDomAndClick(Element element) { - // Add the file input element and click it - // All previous elements will be removed before adding the new one - _target.children.clear(); - _target.children.add(element); - element.click(); - } - List _getXFilesFromFiles(List files) { List xFiles = List(); @@ -180,34 +188,6 @@ class FileSelectorPlugin extends FileSelectorPlatform { return _completer.future; } - - /// Initializes a DOM container where we can host elements. - Element _ensureInitialized(String id) { - var target = querySelector('#${id}'); - if (target == null) { - final Element targetElement = Element.tag('flt-file-picker-inputs') - ..id = id; - - querySelector('body').children.add(targetElement); - target = targetElement; - } - return target; - } - - /// NEW API - - /// Load Helper - Future> _loadFileHelper( - bool multiple, List acceptedTypes) { - final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); - - final FileUploadInputElement element = - createFileInputElement(acceptedTypeString, multiple); - - _addElementToDomAndClick(element); - - return getFilesWhenReady(element); - } } /// Overrides some functions to allow testing From dc76e479a453fd34caeb6c966fc5851ced349065 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 16:26:40 -0700 Subject: [PATCH 054/151] Use simpiler structures in method channels Also update web implementations to async --- .../method_channel_file_selector.dart | 16 +++++++++++----- .../file_selector_web/lib/file_selector_web.dart | 16 ++++------------ 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index 097075c96287..d59160846129 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -17,14 +17,15 @@ class MethodChannelFileSelector extends FileSelectorPlatform { List acceptedTypeGroups, String initialDirectory, String confirmButtonText, - }) { - return _channel.invokeMethod( + }) async { + String path = await _channel.invokeMethod( 'loadFile', { 'acceptedTypes': acceptedTypeGroups, 'initialDirectory': initialDirectory, }, ); + return XFile(path); } /// Load multiple files from user's computer and return it as an XFile @@ -33,14 +34,19 @@ class MethodChannelFileSelector extends FileSelectorPlatform { List acceptedTypeGroups, String initialDirectory, String confirmButtonText, - }) { - return _channel.invokeMethod>( + }) async { + final pathList = await _channel.invokeMethod>( 'loadFiles', { 'acceptedTypes': acceptedTypeGroups, 'initialDirectory': initialDirectory, }, ); + List fileList = List(); + for (String path in pathList) { + fileList.add(XFile(path)); + } + return fileList; } /// Saves the file to user's Disk @@ -51,7 +57,7 @@ class MethodChannelFileSelector extends FileSelectorPlatform { String suggestedName, String confirmButtonText, }) async { - return _channel.invokeMethod( + return _channel.invokeMethod( 'saveFile', { 'initialDirectory': initialDirectory, diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index a378320b5e39..8f964736a28a 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -23,15 +23,8 @@ class FileSelectorPlugin extends FileSelectorPlatform { List acceptedTypeGroups, String initialDirectory, String confirmButtonText, - }) { - Completer _completer = Completer(); - _loadFileHelper(false, acceptedTypeGroups).then((list) { - _completer.complete(list.first); - }).catchError((err) { - _completer.completeError(err); - }); - - return _completer.future; + }) async { + return (await _loadFileHelper(false, acceptedTypeGroups)).first; } /// Open file dialog for loading files and return a XFile @@ -40,7 +33,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { List acceptedTypeGroups, String initialDirectory, String confirmButtonText, - }) { + }) async { return _loadFileHelper(true, acceptedTypeGroups); } @@ -50,8 +43,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { String initialDirectory, String suggestedName, String confirmButtonText, - }) => - Future.value(); + }) async => null; /// Load Helper Future> _loadFileHelper( From ed83bbb9240fecd3a22771e8c9d8fff626f3426e Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 16:40:42 -0700 Subject: [PATCH 055/151] Change some variables to final --- .../file_selector/example/lib/main.dart | 27 +++++++------------ .../test/file_selector_test.dart | 23 +++++++--------- .../lib/src/types/x_file/html.dart | 7 +---- .../lib/file_selector_web.dart | 5 ++-- 4 files changed, 24 insertions(+), 38 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 06ae8125e4d6..8fbd2a0c04bd 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -46,19 +46,14 @@ class _SaveTestState extends State { } void _saveFile() async { - String path = await getSavePath(); + final path = await getSavePath(); - Uint8List data; - data = Uint8List.fromList(_fileController.text.codeUnits); + final data = Uint8List.fromList(_fileController.text.codeUnits); - XFile new_file; - - if (_nameController.text == '') { - new_file = XFile.fromData(data, mimeType: 'text/plain'); - } else { - new_file = XFile.fromData(data, - mimeType: 'text/plain', name: _nameController.text); - } + final new_file = (_nameController.text == '') + ? XFile.fromData(data, mimeType: 'text/plain') + : XFile.fromData(data, + mimeType: 'text/plain', name: _nameController.text); new_file.saveTo(path); } @@ -118,10 +113,9 @@ class LoadTest extends StatefulWidget { class _LoadTestState extends State { void _onLoadImageFile() async { - XTypeGroup typeGroup = - XTypeGroup(label: 'images', extensions: ['jpg', 'png']); + final typeGroup = XTypeGroup(label: 'images', extensions: ['jpg', 'png']); - XFile file = await loadFile(acceptedTypeGroups: [typeGroup]); + final file = await loadFile(acceptedTypeGroups: [typeGroup]); await showDialog( context: context, @@ -131,10 +125,9 @@ class _LoadTestState extends State { } void _onLoadTextFile() async { - XTypeGroup typeGroup = - XTypeGroup(label: 'images', extensions: ['txt', 'json']); + final typeGroup = XTypeGroup(label: 'images', extensions: ['txt', 'json']); - XFile file = await loadFile(acceptedTypeGroups: [typeGroup]); + final file = await loadFile(acceptedTypeGroups: [typeGroup]); await showDialog( context: context, diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index 81b1772fb3bc..2640e91ebfa2 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -9,14 +9,13 @@ import 'package:file_selector/file_selector.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; void main() { - final MockFileSelector mock = MockFileSelector(); + final mock = MockFileSelector(); FileSelectorPlatform.instance = mock; test('getSavePath', () async { - String expectedPath = '/example/path'; + final expectedPath = '/example/path'; - XTypeGroup typeGroup = - XTypeGroup(label: 'group', extensions: ['jpg', 'png']); + final typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); when(mock.getSavePath( acceptedTypeGroups: [typeGroup], @@ -25,7 +24,7 @@ void main() { confirmButtonText: 'save', )).thenAnswer((_) => Future.value(expectedPath)); - String result = await getSavePath( + final result = await getSavePath( acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', suggestedName: 'name', @@ -35,10 +34,9 @@ void main() { }); test('loadFile', () async { - XFile file = XFile('path'); + final file = XFile('path'); - XTypeGroup typeGroup = - XTypeGroup(label: 'group', extensions: ['jpg', 'png']); + final typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); when(mock.loadFile( acceptedTypeGroups: [typeGroup], @@ -46,7 +44,7 @@ void main() { confirmButtonText: 'load', )).thenAnswer((_) => Future.value(file)); - XFile result = await loadFile( + final result = await loadFile( acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', confirmButtonText: 'load'); @@ -55,10 +53,9 @@ void main() { }); test('loadFiles', () async { - XFile file = XFile('path'); + final file = XFile('path'); - XTypeGroup typeGroup = - XTypeGroup(label: 'group', extensions: ['jpg', 'png']); + final typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); when(mock.loadFiles( acceptedTypeGroups: [typeGroup], @@ -66,7 +63,7 @@ void main() { confirmButtonText: 'load', )).thenAnswer((_) => Future.value([file])); - List result = await loadFiles( + final result = await loadFiles( acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', confirmButtonText: 'load'); diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index 2493e9dd2a9d..fe898eb4ca62 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -61,12 +61,7 @@ class XFile extends XFileBase { _lastModified = lastModified, super(path) { if (path == null) { - Blob blob; - if (mimeType == null) { - blob = Blob([bytes]); - } else { - blob = Blob([bytes], mimeType); - } + final blob = (mimeType == null) ? Blob([bytes]) : Blob([bytes], mimeType); this.path = Url.createObjectUrl(blob); } } diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 8f964736a28a..29f3813adfcc 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -43,7 +43,8 @@ class FileSelectorPlugin extends FileSelectorPlatform { String initialDirectory, String suggestedName, String confirmButtonText, - }) async => null; + }) async => + null; /// Load Helper Future> _loadFileHelper( @@ -126,7 +127,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { List _getXFilesFromFiles(List files) { List xFiles = List(); - Duration timeZoneOffset = DateTime.now().timeZoneOffset; + final timeZoneOffset = DateTime.now().timeZoneOffset; for (File file in files) { String url = Url.createObjectUrl(file); From eb19648ea0a8626e1f7be91cc8e90349266d38c7 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 16:52:54 -0700 Subject: [PATCH 056/151] Assert in constructor initializer list instead of body --- .../lib/src/types/x_type_group/x_type_group.dart | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart index 8b02af8ea24e..b4223c8ca77c 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart @@ -23,15 +23,12 @@ class XTypeGroup { this.mimeTypes, this.macUTIs, this.webWildCards, - }) { - if (this.extensions == null && - this.mimeTypes == null && - this.macUTIs == null && - this.webWildCards == null) { - throw ArgumentError( - "At least one type must be provided for an XTypeGroup."); - } - } + }) : assert( + !(extensions == null && + mimeTypes == null && + macUTIs == null && + webWildCards == null), + "At least one type must be provided for an XTypeGroup."); /// The 'name' or reference to this group of types final String label; From bd58fe4b9796a7f4b3ed8e3c2e2867b25378558d Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 17:25:17 -0700 Subject: [PATCH 057/151] Change Error to PlatformException --- .../file_selector_web/lib/file_selector_web.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 29f3813adfcc..71a5a6bc169c 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -3,6 +3,7 @@ import 'dart:html'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:file_selector_platform_interface/src/web_helpers/web_helpers.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:meta/meta.dart'; @@ -174,9 +175,10 @@ class FileSelectorPlugin extends FileSelectorPlatform { }); element.onError.first.then((event) { - if (!_completer.isCompleted) { - _completer.completeError(event); - } + throw PlatformException( + code: 'file_input', + message: "Input element failed on event with target: " + + event?.target?.toString()); }); return _completer.future; From 9213f92757d1d9265a379c0036ffb840730a99bb Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 31 Aug 2020 10:38:54 -0700 Subject: [PATCH 058/151] Update README.md --- packages/file_selector/file_selector/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index 670bdf2012a1..db0f152bc5e2 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -1,3 +1,3 @@ -# file_picker +# file_selector -A Flutter plugin that currently doesn't do anything. +A Flutter plugin that manages files and interactions with file dialogs. From c072c750f3f79fc801f6f551bd3fd73d4e14c4c7 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 1 Sep 2020 15:57:02 -0700 Subject: [PATCH 059/151] Address Some Comments Improvements to code with cleaner dart methods Semantic fixes Reorganize method channels --- .../method_channel_file_selector.dart | 11 ++--- .../file_selector_interface.dart | 4 +- .../lib/file_selector_web.dart | 43 ++++++------------- .../test/test_driver/web_e2e.dart | 6 ++- 4 files changed, 24 insertions(+), 40 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index d59160846129..057697fb1dcd 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -4,7 +4,6 @@ import 'package:flutter/services.dart'; -import '../platform_interface/file_selector_interface.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); @@ -19,10 +18,11 @@ class MethodChannelFileSelector extends FileSelectorPlatform { String confirmButtonText, }) async { String path = await _channel.invokeMethod( - 'loadFile', + 'loadFiles', { 'acceptedTypes': acceptedTypeGroups, 'initialDirectory': initialDirectory, + 'multiple': false, }, ); return XFile(path); @@ -40,13 +40,10 @@ class MethodChannelFileSelector extends FileSelectorPlatform { { 'acceptedTypes': acceptedTypeGroups, 'initialDirectory': initialDirectory, + 'multiple': true, }, ); - List fileList = List(); - for (String path in pathList) { - fileList.add(XFile(path)); - } - return fileList; + return pathList.map((path) => XFile(path)).toList(); } /// Saves the file to user's Disk diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index 66b14c75efd8..e675f0128028 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -51,7 +51,7 @@ abstract class FileSelectorPlatform extends PlatformInterface { String initialDirectory, String confirmButtonText, }) { - throw UnimplementedError('loadFile() has not been implemented.'); + throw UnimplementedError('loadFiles() has not been implemented.'); } /// Open file dialog for saving files and return a file path at which to save @@ -61,6 +61,6 @@ abstract class FileSelectorPlatform extends PlatformInterface { String suggestedName, String confirmButtonText, }) { - throw UnimplementedError('saveFile() has not been implemented.'); + throw UnimplementedError('getSavePath() has not been implemented.'); } } diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 71a5a6bc169c..c52a6e3f7985 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -75,35 +75,21 @@ class FileSelectorPlugin extends FileSelectorPlatform { FileSelectorPlatform.instance = FileSelectorPlugin(); } - void _verifyXTypeGroup(XTypeGroup group) { - if (group.extensions == null && - group.mimeTypes == null && - group.webWildCards == null) { - StateError( - "This XTypeGroup does not have types supported by the web implementation of loadFile."); - } - } - /// Convert list of XTypeGroups to a comma-separated string String _getStringFromFilterGroup(List acceptedTypes) { List allTypes = List(); + for (XTypeGroup group in acceptedTypes ?? []) { - _verifyXTypeGroup(group); - - for (String mimeType in group.mimeTypes ?? []) { - allTypes.add(mimeType); - } - for (String extension in group.extensions ?? []) { - String ext = extension; - if (ext.isNotEmpty && ext[0] != '.') { - ext = '.' + ext; - } - - allTypes.add(ext); - } - for (String webWildCard in group.webWildCards ?? []) { - allTypes.add(webWildCard); - } + assert( + !((group.extensions == null || group.extensions.isEmpty) && + (group.mimeTypes == null || group.mimeTypes.isEmpty) && + (group.webWildCards == null || group.webWildCards.isEmpty)), + 'At least one of extensions / mimeTypes / webWildCards is required for web.'); + + allTypes.addAll(group.extensions + .map((ext) => ext.isNotEmpty && ext[0] != '.' ? '.' + ext : ext)); + allTypes.addAll(group.mimeTypes ?? []); + allTypes.addAll(group.webWildCards ?? []); } return allTypes?.where((e) => e.isNotEmpty)?.join(',') ?? ''; } @@ -175,10 +161,9 @@ class FileSelectorPlugin extends FileSelectorPlatform { }); element.onError.first.then((event) { - throw PlatformException( - code: 'file_input', - message: "Input element failed on event with target: " + - event?.target?.toString()); + ErrorEvent error = event; + _completer.completeError( + PlatformException(code: error.type, message: error.message)); }); return _completer.future; diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index 47515a55a53b..01044c0acce8 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -71,7 +71,7 @@ void main() { expect(result, isNotNull); expect(result.getAttribute('type'), 'file'); expect(result.getAttribute('accept'), - 'plain/text,application/json,.txt,.json'); + '.txt,.json,plain/text,application/json'); }); testWidgets('input element is clicked', (WidgetTester tester) async { @@ -143,10 +143,12 @@ void main() { (element) => element.tagName == 'INPUT', orElse: () => null); + print (result.getAttribute('accept')); + expect(result, isNotNull); expect(result.getAttribute('type'), 'file'); expect(result.getAttribute('accept'), - 'plain/text,application/json,.txt,.json'); + '.txt,.json,plain/text,application/json'); expect(result.multiple, true); }); From 38a7ea06ce058917aa91ccf023780296a70493fa Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 15 Jul 2020 18:35:39 -0700 Subject: [PATCH 060/151] "Hello World" file_picker plugin --- packages/file_picker/file_picker/CHANGELOG.md | 3 + packages/file_picker/file_picker/LICENSE | 27 ++++++ packages/file_picker/file_picker/README.md | 3 + .../file_picker/example/.gitignore | 48 +++++++++++ .../file_picker/file_picker/example/.metadata | 10 +++ .../file_picker/file_picker/example/README.md | 16 ++++ .../file_picker/example/lib/main.dart | 71 ++++++++++++++++ .../file_picker/example/pubspec.yaml | 79 ++++++++++++++++++ .../file_picker/example/test/widget_test.dart | 30 +++++++ .../file_picker/example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../file_picker/example/web/index.html | 33 ++++++++ .../file_picker/example/web/manifest.json | 23 +++++ .../file_picker/lib/file_picker.dart | 14 ++++ packages/file_picker/file_picker/pubspec.yaml | 32 +++++++ .../file_picker/test/file_picker_test.dart | 8 ++ .../CHANGELOG.md | 3 + .../file_picker_platform_interface/LICENSE | 27 ++++++ .../file_picker_platform_interface/README.md | 26 ++++++ .../lib/file_picker_platform_interface.dart | 43 ++++++++++ .../lib/method_channel_file_picker.dart | 21 +++++ .../pubspec.yaml | 22 +++++ .../test/method_channel_file_picker.dart | 14 ++++ .../file_picker/file_picker_web/CHANGELOG.md | 3 + packages/file_picker/file_picker_web/LICENSE | 27 ++++++ .../file_picker/file_picker_web/README.md | 37 ++++++++ .../file_picker_web/lib/file_picker_web.dart | 20 +++++ .../file_picker/file_picker_web/pubspec.yaml | 35 ++++++++ .../test/file_picker_web_test.dart | 17 ++++ 30 files changed, 692 insertions(+) create mode 100644 packages/file_picker/file_picker/CHANGELOG.md create mode 100644 packages/file_picker/file_picker/LICENSE create mode 100644 packages/file_picker/file_picker/README.md create mode 100644 packages/file_picker/file_picker/example/.gitignore create mode 100644 packages/file_picker/file_picker/example/.metadata create mode 100644 packages/file_picker/file_picker/example/README.md create mode 100644 packages/file_picker/file_picker/example/lib/main.dart create mode 100644 packages/file_picker/file_picker/example/pubspec.yaml create mode 100644 packages/file_picker/file_picker/example/test/widget_test.dart create mode 100644 packages/file_picker/file_picker/example/web/favicon.png create mode 100644 packages/file_picker/file_picker/example/web/icons/Icon-192.png create mode 100644 packages/file_picker/file_picker/example/web/icons/Icon-512.png create mode 100644 packages/file_picker/file_picker/example/web/index.html create mode 100644 packages/file_picker/file_picker/example/web/manifest.json create mode 100644 packages/file_picker/file_picker/lib/file_picker.dart create mode 100644 packages/file_picker/file_picker/pubspec.yaml create mode 100644 packages/file_picker/file_picker/test/file_picker_test.dart create mode 100644 packages/file_picker/file_picker_platform_interface/CHANGELOG.md create mode 100644 packages/file_picker/file_picker_platform_interface/LICENSE create mode 100644 packages/file_picker/file_picker_platform_interface/README.md create mode 100644 packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/method_channel_file_picker.dart create mode 100644 packages/file_picker/file_picker_platform_interface/pubspec.yaml create mode 100644 packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart create mode 100644 packages/file_picker/file_picker_web/CHANGELOG.md create mode 100644 packages/file_picker/file_picker_web/LICENSE create mode 100644 packages/file_picker/file_picker_web/README.md create mode 100644 packages/file_picker/file_picker_web/lib/file_picker_web.dart create mode 100644 packages/file_picker/file_picker_web/pubspec.yaml create mode 100644 packages/file_picker/file_picker_web/test/file_picker_web_test.dart diff --git a/packages/file_picker/file_picker/CHANGELOG.md b/packages/file_picker/file_picker/CHANGELOG.md new file mode 100644 index 000000000000..5703fc8bcbb3 --- /dev/null +++ b/packages/file_picker/file_picker/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.1.0 + +* Initial Open Source release. diff --git a/packages/file_picker/file_picker/LICENSE b/packages/file_picker/file_picker/LICENSE new file mode 100644 index 000000000000..000b4618d2bd --- /dev/null +++ b/packages/file_picker/file_picker/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/packages/file_picker/file_picker/README.md b/packages/file_picker/file_picker/README.md new file mode 100644 index 000000000000..670bdf2012a1 --- /dev/null +++ b/packages/file_picker/file_picker/README.md @@ -0,0 +1,3 @@ +# file_picker + +A Flutter plugin that currently doesn't do anything. diff --git a/packages/file_picker/file_picker/example/.gitignore b/packages/file_picker/file_picker/example/.gitignore new file mode 100644 index 000000000000..7abd0753cfc3 --- /dev/null +++ b/packages/file_picker/file_picker/example/.gitignore @@ -0,0 +1,48 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Currently only web supported +android/ +ios/ + +# Exceptions to above rules. +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/packages/file_picker/file_picker/example/.metadata b/packages/file_picker/file_picker/example/.metadata new file mode 100644 index 000000000000..897381f2373f --- /dev/null +++ b/packages/file_picker/file_picker/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 7736f3bc90270dcb0480db2ccffbf1d13c28db85 + channel: dev + +project_type: app diff --git a/packages/file_picker/file_picker/example/README.md b/packages/file_picker/file_picker/example/README.md new file mode 100644 index 000000000000..a13562602822 --- /dev/null +++ b/packages/file_picker/file_picker/example/README.md @@ -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. diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart new file mode 100644 index 000000000000..00edac95ff8f --- /dev/null +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:file_picker/file_picker.dart'; + +void main() { + runApp(MyApp()); +} + +/// MyApp is the Main Application +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: MyHomePage(title: 'File Picker Demo Home Page'), + ); + } +} + +/// Home Page of the application +class MyHomePage extends StatefulWidget { + + /// Constructor for MyHomePage + MyHomePage({Key key, this.title}) : super(key: key); + + /// Title of Home Page + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +/// State of Home Page +class _MyHomePageState extends State { + String _msg = ""; + + void _getMessage() async { + String msg = await getMessage(); + setState(() { + this._msg = "Here is your message: " + msg; + }); + } + + @override + Widget build(BuildContext context) { + + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + this._msg + ), + SizedBox(height: 10), + RaisedButton( + child: Text('Press for a message'), + onPressed: () => { _getMessage() }, + ), + ], + ), + ), + ); + } +} diff --git a/packages/file_picker/file_picker/example/pubspec.yaml b/packages/file_picker/file_picker/example/pubspec.yaml new file mode 100644 index 000000000000..baa9292c63cc --- /dev/null +++ b/packages/file_picker/file_picker/example/pubspec.yaml @@ -0,0 +1,79 @@ +name: example +description: A new Flutter project. + +# The following line prevents the package from being accidentally published to +# pub.dev using `pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=2.7.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + + file_picker: + path: ../ + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^0.1.3 + +dev_dependencies: + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, 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 from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/file_picker/file_picker/example/test/widget_test.dart b/packages/file_picker/file_picker/example/test/widget_test.dart new file mode 100644 index 000000000000..747db1da35e8 --- /dev/null +++ b/packages/file_picker/file_picker/example/test/widget_test.dart @@ -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); + }); +} diff --git a/packages/file_picker/file_picker/example/web/favicon.png b/packages/file_picker/file_picker/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/packages/file_picker/file_picker/example/web/icons/Icon-192.png b/packages/file_picker/file_picker/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 zcmZ`-2T+sGz6~)*FVZ`aW+(v>MIm&M-g^@e2u-B-DoB?qO+b1Tq<5uCCv>ESfRum& zp%X;f!~1{tzL__3=gjVJ=j=J>+nMj%ncXj1Q(b|Ckbw{Y0FWpt%4y%$uD=Z*c-x~o zE;IoE;xa#7Ll5nj-e4CuXB&G*IM~D21rCP$*xLXAK8rIMCSHuSu%bL&S3)8YI~vyp@KBu9Ph7R_pvKQ@xv>NQ`dZp(u{Z8K3yOB zn7-AR+d2JkW)KiGx0hosml;+eCXp6+w%@STjFY*CJ?udJ64&{BCbuebcuH;}(($@@ znNlgBA@ZXB)mcl9nbX#F!f_5Z=W>0kh|UVWnf!At4V*LQP%*gPdCXd6P@J4Td;!Ur z<2ZLmwr(NG`u#gDEMP19UcSzRTL@HsK+PnIXbVBT@oHm53DZr?~V(0{rsalAfwgo zEh=GviaqkF;}F_5-yA!1u3!gxaR&Mj)hLuj5Q-N-@Lra{%<4ONja8pycD90&>yMB` zchhd>0CsH`^|&TstH-8+R`CfoWqmTTF_0?zDOY`E`b)cVi!$4xA@oO;SyOjJyP^_j zx^@Gdf+w|FW@DMdOi8=4+LJl$#@R&&=UM`)G!y%6ZzQLoSL%*KE8IO0~&5XYR9 z&N)?goEiWA(YoRfT{06&D6Yuu@Qt&XVbuW@COb;>SP9~aRc+z`m`80pB2o%`#{xD@ zI3RAlukL5L>px6b?QW1Ac_0>ew%NM!XB2(H+1Y3AJC?C?O`GGs`331Nd4ZvG~bMo{lh~GeL zSL|tT*fF-HXxXYtfu5z+T5Mx9OdP7J4g%@oeC2FaWO1D{=NvL|DNZ}GO?O3`+H*SI z=grGv=7dL{+oY0eJFGO!Qe(e2F?CHW(i!!XkGo2tUvsQ)I9ev`H&=;`N%Z{L zO?vV%rDv$y(@1Yj@xfr7Kzr<~0{^T8wM80xf7IGQF_S-2c0)0D6b0~yD7BsCy+(zL z#N~%&e4iAwi4F$&dI7x6cE|B{f@lY5epaDh=2-(4N05VO~A zQT3hanGy_&p+7Fb^I#ewGsjyCEUmSCaP6JDB*=_()FgQ(-pZ28-{qx~2foO4%pM9e z*_63RT8XjgiaWY|*xydf;8MKLd{HnfZ2kM%iq}fstImB-K6A79B~YoPVa@tYN@T_$ zea+9)<%?=Fl!kd(Y!G(-o}ko28hg2!MR-o5BEa_72uj7Mrc&{lRh3u2%Y=Xk9^-qa zBPWaD=2qcuJ&@Tf6ue&)4_V*45=zWk@Z}Q?f5)*z)-+E|-yC4fs5CE6L_PH3=zI8p z*Z3!it{1e5_^(sF*v=0{`U9C741&lub89gdhKp|Y8CeC{_{wYK-LSbp{h)b~9^j!s z7e?Y{Z3pZv0J)(VL=g>l;<}xk=T*O5YR|hg0eg4u98f2IrA-MY+StQIuK-(*J6TRR z|IM(%uI~?`wsfyO6Tgmsy1b3a)j6M&-jgUjVg+mP*oTKdHg?5E`!r`7AE_#?Fc)&a z08KCq>Gc=ne{PCbRvs6gVW|tKdcE1#7C4e`M|j$C5EYZ~Y=jUtc zj`+?p4ba3uy7><7wIokM79jPza``{Lx0)zGWg;FW1^NKY+GpEi=rHJ+fVRGfXO zPHV52k?jxei_!YYAw1HIz}y8ZMwdZqU%ESwMn7~t zdI5%B;U7RF=jzRz^NuY9nM)&<%M>x>0(e$GpU9th%rHiZsIT>_qp%V~ILlyt^V`=d z!1+DX@ah?RnB$X!0xpTA0}lN@9V-ePx>wQ?-xrJr^qDlw?#O(RsXeAvM%}rg0NT#t z!CsT;-vB=B87ShG`GwO;OEbeL;a}LIu=&@9cb~Rsx(ZPNQ!NT7H{@j0e(DiLea>QD zPmpe90gEKHEZ8oQ@6%E7k-Ptn#z)b9NbD@_GTxEhbS+}Bb74WUaRy{w;E|MgDAvHw zL)ycgM7mB?XVh^OzbC?LKFMotw3r@i&VdUV%^Efdib)3@soX%vWCbnOyt@Y4swW925@bt45y0HY3YI~BnnzZYrinFy;L?2D3BAL`UQ zEj))+f>H7~g8*VuWQ83EtGcx`hun$QvuurSMg3l4IP8Fe`#C|N6mbYJ=n;+}EQm;< z!!N=5j1aAr_uEnnzrEV%_E|JpTb#1p1*}5!Ce!R@d$EtMR~%9# zd;h8=QGT)KMW2IKu_fA_>p_und#-;Q)p%%l0XZOXQicfX8M~7?8}@U^ihu;mizj)t zgV7wk%n-UOb z#!P5q?Ex+*Kx@*p`o$q8FWL*E^$&1*!gpv?Za$YO~{BHeGY*5%4HXUKa_A~~^d z=E*gf6&+LFF^`j4$T~dR)%{I)T?>@Ma?D!gi9I^HqvjPc3-v~=qpX1Mne@*rzT&Xw zQ9DXsSV@PqpEJO-g4A&L{F&;K6W60D!_vs?Vx!?w27XbEuJJP&);)^+VF1nHqHBWu z^>kI$M9yfOY8~|hZ9WB!q-9u&mKhEcRjlf2nm_@s;0D#c|@ED7NZE% zzR;>P5B{o4fzlfsn3CkBK&`OSb-YNrqx@N#4CK!>bQ(V(D#9|l!e9(%sz~PYk@8zt zPN9oK78&-IL_F zhsk1$6p;GqFbtB^ZHHP+cjMvA0(LqlskbdYE_rda>gvQLTiqOQ1~*7lg%z*&p`Ry& zRcG^DbbPj_jOKHTr8uk^15Boj6>hA2S-QY(W-6!FIq8h$<>MI>PYYRenQDBamO#Fv zAH5&ImqKBDn0v5kb|8i0wFhUBJTpT!rB-`zK)^SNnRmLraZcPYK7b{I@+}wXVdW-{Ps17qdRA3JatEd?rPV z4@}(DAMf5EqXCr4-B+~H1P#;t@O}B)tIJ(W6$LrK&0plTmnPpb1TKn3?f?Kk``?D+ zQ!MFqOX7JbsXfQrz`-M@hq7xlfNz;_B{^wbpG8des56x(Q)H)5eLeDwCrVR}hzr~= zM{yXR6IM?kXxauLza#@#u?Y|o;904HCqF<8yT~~c-xyRc0-vxofnxG^(x%>bj5r}N zyFT+xnn-?B`ohA>{+ZZQem=*Xpqz{=j8i2TAC#x-m;;mo{{sLB_z(UoAqD=A#*juZ zCv=J~i*O8;F}A^Wf#+zx;~3B{57xtoxC&j^ie^?**T`WT2OPRtC`xj~+3Kprn=rVM zVJ|h5ux%S{dO}!mq93}P+h36mZ5aZg1-?vhL$ke1d52qIiXSE(llCr5i=QUS?LIjc zV$4q=-)aaR4wsrQv}^shL5u%6;`uiSEs<1nG^?$kl$^6DL z43CjY`M*p}ew}}3rXc7Xck@k41jx}c;NgEIhKZ*jsBRZUP-x2cm;F1<5$jefl|ppO zmZd%%?gMJ^g9=RZ^#8Mf5aWNVhjAS^|DQO+q$)oeob_&ZLFL(zur$)); zU19yRm)z<4&4-M}7!9+^Wl}Uk?`S$#V2%pQ*SIH5KI-mn%i;Z7-)m$mN9CnI$G7?# zo`zVrUwoSL&_dJ92YhX5TKqaRkfPgC4=Q&=K+;_aDs&OU0&{WFH}kKX6uNQC6%oUH z2DZa1s3%Vtk|bglbxep-w)PbFG!J17`<$g8lVhqD2w;Z0zGsh-r zxZ13G$G<48leNqR!DCVt9)@}(zMI5w6Wo=N zpP1*3DI;~h2WDWgcKn*f!+ORD)f$DZFwgKBafEZmeXQMAsq9sxP9A)7zOYnkHT9JU zRA`umgmP9d6=PHmFIgx=0$(sjb>+0CHG)K@cPG{IxaJ&Ueo8)0RWgV9+gO7+Bl1(F z7!BslJ2MP*PWJ;x)QXbR$6jEr5q3 z(3}F@YO_P1NyTdEXRLU6fp?9V2-S=E+YaeLL{Y)W%6`k7$(EW8EZSA*(+;e5@jgD^I zaJQ2|oCM1n!A&-8`;#RDcZyk*+RPkn_r8?Ak@agHiSp*qFNX)&i21HE?yuZ;-C<3C zwJGd1lx5UzViP7sZJ&|LqH*mryb}y|%AOw+v)yc`qM)03qyyrqhX?ub`Cjwx2PrR! z)_z>5*!*$x1=Qa-0uE7jy0z`>|Ni#X+uV|%_81F7)b+nf%iz=`fF4g5UfHS_?PHbr zB;0$bK@=di?f`dS(j{l3-tSCfp~zUuva+=EWxJcRfp(<$@vd(GigM&~vaYZ0c#BTs z3ijkxMl=vw5AS&DcXQ%eeKt!uKvh2l3W?&3=dBHU=Gz?O!40S&&~ei2vg**c$o;i89~6DVns zG>9a*`k5)NI9|?W!@9>rzJ;9EJ=YlJTx1r1BA?H`LWijk(rTax9(OAu;q4_wTj-yj z1%W4GW&K4T=uEGb+E!>W0SD_C0RR91 literal 0 HcmV?d00001 diff --git a/packages/file_picker/file_picker/example/web/icons/Icon-512.png b/packages/file_picker/file_picker/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 zcmd5=2T+s!lYZ%-(h(2@5fr2dC?F^$C=i-}R6$UX8af(!je;W5yC_|HmujSgN*6?W z3knF*TL1$|?oD*=zPbBVex*RUIKsL<(&Rj9%^UD2IK3W?2j>D?eWQgvS-HLymHo9%~|N2Q{~j za?*X-{b9JRowv_*Mh|;*-kPFn>PI;r<#kFaxFqbn?aq|PduQg=2Q;~Qc}#z)_T%x9 zE|0!a70`58wjREmAH38H1)#gof)U3g9FZ^ zF7&-0^Hy{4XHWLoC*hOG(dg~2g6&?-wqcpf{ z&3=o8vw7lMi22jCG9RQbv8H}`+}9^zSk`nlR8?Z&G2dlDy$4#+WOlg;VHqzuE=fM@ z?OI6HEJH4&tA?FVG}9>jAnq_^tlw8NbjNhfqk2rQr?h(F&WiKy03Sn=-;ZJRh~JrD zbt)zLbnabttEZ>zUiu`N*u4sfQaLE8-WDn@tHp50uD(^r-}UsUUu)`!Rl1PozAc!a z?uj|2QDQ%oV-jxUJmJycySBINSKdX{kDYRS=+`HgR2GO19fg&lZKyBFbbXhQV~v~L za^U944F1_GtuFXtvDdDNDvp<`fqy);>Vw=ncy!NB85Tw{&sT5&Ox%-p%8fTS;OzlRBwErvO+ROe?{%q-Zge=%Up|D4L#>4K@Ke=x%?*^_^P*KD zgXueMiS63!sEw@fNLB-i^F|@Oib+S4bcy{eu&e}Xvb^(mA!=U=Xr3||IpV~3K zQWzEsUeX_qBe6fky#M zzOJm5b+l;~>=sdp%i}}0h zO?B?i*W;Ndn02Y0GUUPxERG`3Bjtj!NroLoYtyVdLtl?SE*CYpf4|_${ku2s`*_)k zN=a}V8_2R5QANlxsq!1BkT6$4>9=-Ix4As@FSS;1q^#TXPrBsw>hJ}$jZ{kUHoP+H zvoYiR39gX}2OHIBYCa~6ERRPJ#V}RIIZakUmuIoLF*{sO8rAUEB9|+A#C|@kw5>u0 zBd=F!4I)Be8ycH*)X1-VPiZ+Ts8_GB;YW&ZFFUo|Sw|x~ZajLsp+_3gv((Q#N>?Jz zFBf`~p_#^${zhPIIJY~yo!7$-xi2LK%3&RkFg}Ax)3+dFCjGgKv^1;lUzQlPo^E{K zmCnrwJ)NuSaJEmueEPO@(_6h3f5mFffhkU9r8A8(JC5eOkux{gPmx_$Uv&|hyj)gN zd>JP8l2U&81@1Hc>#*su2xd{)T`Yw< zN$dSLUN}dfx)Fu`NcY}TuZ)SdviT{JHaiYgP4~@`x{&h*Hd>c3K_To9BnQi@;tuoL z%PYQo&{|IsM)_>BrF1oB~+`2_uZQ48z9!)mtUR zdfKE+b*w8cPu;F6RYJiYyV;PRBbThqHBEu_(U{(gGtjM}Zi$pL8Whx}<JwE3RM0F8x7%!!s)UJVq|TVd#hf1zVLya$;mYp(^oZQ2>=ZXU1c$}f zm|7kfk>=4KoQoQ!2&SOW5|JP1)%#55C$M(u4%SP~tHa&M+=;YsW=v(Old9L3(j)`u z2?#fK&1vtS?G6aOt@E`gZ9*qCmyvc>Ma@Q8^I4y~f3gs7*d=ATlP>1S zyF=k&6p2;7dn^8?+!wZO5r~B+;@KXFEn^&C=6ma1J7Au6y29iMIxd7#iW%=iUzq&C=$aPLa^Q zncia$@TIy6UT@69=nbty5epP>*fVW@5qbUcb2~Gg75dNd{COFLdiz3}kODn^U*=@E z0*$7u7Rl2u)=%fk4m8EK1ctR!6%Ve`e!O20L$0LkM#f+)n9h^dn{n`T*^~d+l*Qlx z$;JC0P9+en2Wlxjwq#z^a6pdnD6fJM!GV7_%8%c)kc5LZs_G^qvw)&J#6WSp< zmsd~1-(GrgjC56Pdf6#!dt^y8Rg}!#UXf)W%~PeU+kU`FeSZHk)%sFv++#Dujk-~m zFHvVJC}UBn2jN& zs!@nZ?e(iyZPNo`p1i#~wsv9l@#Z|ag3JR>0#u1iW9M1RK1iF6-RbJ4KYg?B`dET9 zyR~DjZ>%_vWYm*Z9_+^~hJ_|SNTzBKx=U0l9 z9x(J96b{`R)UVQ$I`wTJ@$_}`)_DyUNOso6=WOmQKI1e`oyYy1C&%AQU<0-`(ow)1 zT}gYdwWdm4wW6|K)LcfMe&psE0XGhMy&xS`@vLi|1#Za{D6l@#D!?nW87wcscUZgELT{Cz**^;Zb~7 z(~WFRO`~!WvyZAW-8v!6n&j*PLm9NlN}BuUN}@E^TX*4Or#dMMF?V9KBeLSiLO4?B zcE3WNIa-H{ThrlCoN=XjOGk1dT=xwwrmt<1a)mrRzg{35`@C!T?&_;Q4Ce=5=>z^*zE_c(0*vWo2_#TD<2)pLXV$FlwP}Ik74IdDQU@yhkCr5h zn5aa>B7PWy5NQ!vf7@p_qtC*{dZ8zLS;JetPkHi>IvPjtJ#ThGQD|Lq#@vE2xdl%`x4A8xOln}BiQ92Po zW;0%A?I5CQ_O`@Ad=`2BLPPbBuPUp@Hb%a_OOI}y{Rwa<#h z5^6M}s7VzE)2&I*33pA>e71d78QpF>sNK;?lj^Kl#wU7G++`N_oL4QPd-iPqBhhs| z(uVM}$ItF-onXuuXO}o$t)emBO3Hjfyil@*+GF;9j?`&67GBM;TGkLHi>@)rkS4Nj zAEk;u)`jc4C$qN6WV2dVd#q}2X6nKt&X*}I@jP%Srs%%DS92lpDY^K*Sx4`l;aql$ zt*-V{U&$DM>pdO?%jt$t=vg5|p+Rw?SPaLW zB6nvZ69$ne4Z(s$3=Rf&RX8L9PWMV*S0@R zuIk&ba#s6sxVZ51^4Kon46X^9`?DC9mEhWB3f+o4#2EXFqy0(UTc>GU| zGCJmI|Dn-dX#7|_6(fT)>&YQ0H&&JX3cTvAq(a@ydM4>5Njnuere{J8p;3?1az60* z$1E7Yyxt^ytULeokgDnRVKQw9vzHg1>X@@jM$n$HBlveIrKP5-GJq%iWH#odVwV6cF^kKX(@#%%uQVb>#T6L^mC@)%SMd4DF? zVky!~ge27>cpUP1Vi}Z32lbLV+CQy+T5Wdmva6Fg^lKb!zrg|HPU=5Qu}k;4GVH+x z%;&pN1LOce0w@9i1Mo-Y|7|z}fbch@BPp2{&R-5{GLoeu8@limQmFF zaJRR|^;kW_nw~0V^ zfTnR!Ni*;-%oSHG1yItARs~uxra|O?YJxBzLjpeE-=~TO3Dn`JL5Gz;F~O1u3|FE- zvK2Vve`ylc`a}G`gpHg58Cqc9fMoy1L}7x7T>%~b&irrNMo?np3`q;d3d;zTK>nrK zOjPS{@&74-fA7j)8uT9~*g23uGnxwIVj9HorzUX#s0pcp2?GH6i}~+kv9fWChtPa_ z@T3m+$0pbjdQw7jcnHn;Pi85hk_u2-1^}c)LNvjdam8K-XJ+KgKQ%!?2n_!#{$H|| zLO=%;hRo6EDmnOBKCL9Cg~ETU##@u^W_5joZ%Et%X_n##%JDOcsO=0VL|Lkk!VdRJ z^|~2pB@PUspT?NOeO?=0Vb+fAGc!j%Ufn-cB`s2A~W{Zj{`wqWq_-w0wr@6VrM zbzni@8c>WS!7c&|ZR$cQ;`niRw{4kG#e z70e!uX8VmP23SuJ*)#(&R=;SxGAvq|&>geL&!5Z7@0Z(No*W561n#u$Uc`f9pD70# z=sKOSK|bF~#khTTn)B28h^a1{;>EaRnHj~>i=Fnr3+Fa4 z`^+O5_itS#7kPd20rq66_wH`%?HNzWk@XFK0n;Z@Cx{kx==2L22zWH$Yg?7 zvDj|u{{+NR3JvUH({;b*$b(U5U z7(lF!1bz2%06+|-v(D?2KgwNw7( zJB#Tz+ZRi&U$i?f34m7>uTzO#+E5cbaiQ&L}UxyOQq~afbNB4EI{E04ZWg53w0A{O%qo=lF8d zf~ktGvIgf-a~zQoWf>loF7pOodrd0a2|BzwwPDV}ShauTK8*fmF6NRbO>Iw9zZU}u zw8Ya}?seBnEGQDmH#XpUUkj}N49tP<2jYwTFp!P+&Fd(%Z#yo80|5@zN(D{_pNow*&4%ql zW~&yp@scb-+Qj-EmErY+Tu=dUmf@*BoXY2&oKT8U?8?s1d}4a`Aq>7SV800m$FE~? zjmz(LY+Xx9sDX$;vU`xgw*jLw7dWOnWWCO8o|;}f>cu0Q&`0I{YudMn;P;L3R-uz# zfns_mZED_IakFBPP2r_S8XM$X)@O-xVKi4`7373Jkd5{2$M#%cRhWer3M(vr{S6>h zj{givZJ3(`yFL@``(afn&~iNx@B1|-qfYiZu?-_&Z8+R~v`d6R-}EX9IVXWO-!hL5 z*k6T#^2zAXdardU3Ao~I)4DGdAv2bx{4nOK`20rJo>rmk3S2ZDu}))8Z1m}CKigf0 z3L`3Y`{huj`xj9@`$xTZzZc3je?n^yG<8sw$`Y%}9mUsjUR%T!?k^(q)6FH6Af^b6 zlPg~IEwg0y;`t9y;#D+uz!oE4VP&Je!<#q*F?m5L5?J3i@!0J6q#eu z!RRU`-)HeqGi_UJZ(n~|PSNsv+Wgl{P-TvaUQ9j?ZCtvb^37U$sFpBrkT{7Jpd?HpIvj2!}RIq zH{9~+gErN2+}J`>Jvng2hwM`=PLNkc7pkjblKW|+Fk9rc)G1R>Ww>RC=r-|!m-u7( zc(a$9NG}w#PjWNMS~)o=i~WA&4L(YIW25@AL9+H9!?3Y}sv#MOdY{bb9j>p`{?O(P zIvb`n?_(gP2w3P#&91JX*md+bBEr%xUHMVqfB;(f?OPtMnAZ#rm5q5mh;a2f_si2_ z3oXWB?{NF(JtkAn6F(O{z@b76OIqMC$&oJ_&S|YbFJ*)3qVX_uNf5b8(!vGX19hsG z(OP>RmZp29KH9Ge2kKjKigUmOe^K_!UXP`von)PR8Qz$%=EmOB9xS(ZxE_tnyzo}7 z=6~$~9k0M~v}`w={AeqF?_)9q{m8K#6M{a&(;u;O41j)I$^T?lx5(zlebpY@NT&#N zR+1bB)-1-xj}R8uwqwf=iP1GbxBjneCC%UrSdSxK1vM^i9;bUkS#iRZw2H>rS<2<$ zNT3|sDH>{tXb=zq7XZi*K?#Zsa1h1{h5!Tq_YbKFm_*=A5-<~j63he;4`77!|LBlo zR^~tR3yxcU=gDFbshyF6>o0bdp$qmHS7D}m3;^QZq9kBBU|9$N-~oU?G5;jyFR7>z hN`IR97YZXIo@y!QgFWddJ3|0`sjFx!m))><{BI=FK%f8s literal 0 HcmV?d00001 diff --git a/packages/file_picker/file_picker/example/web/index.html b/packages/file_picker/file_picker/example/web/index.html new file mode 100644 index 000000000000..9b7a438f823a --- /dev/null +++ b/packages/file_picker/file_picker/example/web/index.html @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + example + + + + + + + + diff --git a/packages/file_picker/file_picker/example/web/manifest.json b/packages/file_picker/file_picker/example/web/manifest.json new file mode 100644 index 000000000000..8c012917dab7 --- /dev/null +++ b/packages/file_picker/file_picker/example/web/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "example", + "short_name": "example", + "start_url": ".", + "display": "standalone", + "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" + } + ] +} diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart new file mode 100644 index 000000000000..2b712eb127c7 --- /dev/null +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -0,0 +1,14 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; + +/// Gets message from platform implementation +Future getMessage() async { + final String result = await FilePickerPlatform.instance.getMessage(); + return result; +} + diff --git a/packages/file_picker/file_picker/pubspec.yaml b/packages/file_picker/file_picker/pubspec.yaml new file mode 100644 index 000000000000..4679320c134a --- /dev/null +++ b/packages/file_picker/file_picker/pubspec.yaml @@ -0,0 +1,32 @@ +name: file_picker +description: Flutter plugin for launching a URL on Android and iOS. Supports + web, phone, SMS, and email schemes. +homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher +version: 0.1.0 + +flutter: + plugin: + platforms: + web: + default_package: file_picker_web + +dependencies: + flutter: + sdk: flutter + file_picker_platform_interface: + path: ../file_picker_platform_interface + file_picker_web: + path: ../file_picker_web + + +dev_dependencies: + flutter_test: + sdk: flutter + test: ^1.3.0 + mockito: ^4.1.1 + plugin_platform_interface: ^1.0.0 + pedantic: ^1.8.0 + +environment: + sdk: ">=2.1.0 <3.0.0" + flutter: ">=1.12.13+hotfix.5 <2.0.0" diff --git a/packages/file_picker/file_picker/test/file_picker_test.dart b/packages/file_picker/file_picker/test/file_picker_test.dart new file mode 100644 index 000000000000..6940fcfb4a6e --- /dev/null +++ b/packages/file_picker/file_picker/test/file_picker_test.dart @@ -0,0 +1,8 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +void main() { +} + diff --git a/packages/file_picker/file_picker_platform_interface/CHANGELOG.md b/packages/file_picker/file_picker_platform_interface/CHANGELOG.md new file mode 100644 index 000000000000..6073234226b2 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.1.0 + +* Initial release. diff --git a/packages/file_picker/file_picker_platform_interface/LICENSE b/packages/file_picker/file_picker_platform_interface/LICENSE new file mode 100644 index 000000000000..c89293372cf3 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/file_picker/file_picker_platform_interface/README.md b/packages/file_picker/file_picker_platform_interface/README.md new file mode 100644 index 000000000000..498936296468 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/README.md @@ -0,0 +1,26 @@ +# file_picker_platform_interface + +A common platform interface for the `file_picker` plugin. + +This interface allows platform-specific implementations of the `file_picker` +plugin, as well as the plugin itself, to ensure they are supporting the +same interface. + +# Usage + +To implement a new platform-specific implementation of `file_picker`, extend +[`FilePickerPlatform`][2] with an implementation that performs the +platform-specific behavior, and when you register your plugin, set the default +`FilePickerPlatform` by calling +`FilePickerPlatform.instance = MyPlatformFilePicker()`. + +# Note on breaking changes + +Strongly prefer non-breaking changes (such as adding a method to the interface) +over breaking changes for this package. + +See https://flutter.dev/go/platform-interface-breaking-changes for a discussion +on why a less-clean interface is preferable to a breaking change. + +[1]: ../file_picker +[2]: lib/file_picker_platform_interface.dart diff --git a/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart new file mode 100644 index 000000000000..d0f557cff059 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart @@ -0,0 +1,43 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'method_channel_file_picker.dart'; + +/// 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 +/// [FilePickerPlatform] methods. +abstract class FilePickerPlatform extends PlatformInterface { + /// Constructs a FilePickerPlatform. + FilePickerPlatform() : super(token: _token); + + static final Object _token = Object(); + + static FilePickerPlatform _instance = MethodChannelFilePicker(); + + /// The default instance of [FilePickerPlatform] to use. + /// + /// Defaults to [MethodChannelFilePicker]. + static FilePickerPlatform get instance => _instance; + + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [FilePickerPlatform] when they register themselves. + static set instance(FilePickerPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + + /// Returns the message from each platform implementation + Future getMessage() { + throw UnimplementedError('getMessage() has not been implemented.'); + } +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/lib/method_channel_file_picker.dart new file mode 100644 index 000000000000..debaaab8b15e --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/method_channel_file_picker.dart @@ -0,0 +1,21 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:meta/meta.dart' show required; + +import 'file_picker_platform_interface.dart'; + +const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); + +/// An implementation of [FilePickerPlatform] that uses method channels. +class MethodChannelFilePicker extends FilePickerPlatform { + + @override + Future getMessage() { + return _channel.invokeMethod('getMessage'); + } +} diff --git a/packages/file_picker/file_picker_platform_interface/pubspec.yaml b/packages/file_picker/file_picker_platform_interface/pubspec.yaml new file mode 100644 index 000000000000..359de184bfc2 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/pubspec.yaml @@ -0,0 +1,22 @@ +name: file_picker_platform_interface +description: A common platform interface for the file_picker plugin. +homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_platform_interface +# NOTE: We strongly prefer non-breaking changes, even at the expense of a +# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes +version: 0.1.0 + +dependencies: + flutter: + sdk: flutter + meta: ^1.0.5 + plugin_platform_interface: ^1.0.1 + +dev_dependencies: + flutter_test: + sdk: flutter + mockito: ^4.1.1 + pedantic: ^1.8.0 + +environment: + sdk: ">=2.1.0 <3.0.0" + flutter: ">=1.9.1+hotfix.4 <2.0.0" diff --git a/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart new file mode 100644 index 000000000000..cb0f11a529f9 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart @@ -0,0 +1,14 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:mockito/mockito.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'package:file_picker_platform_interface/method_channel_file_picker.dart'; +import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; + +void main() { +} \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/CHANGELOG.md b/packages/file_picker/file_picker_web/CHANGELOG.md new file mode 100644 index 000000000000..7be26166a1f2 --- /dev/null +++ b/packages/file_picker/file_picker_web/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.1.0 + +- Initial open-source release. diff --git a/packages/file_picker/file_picker_web/LICENSE b/packages/file_picker/file_picker_web/LICENSE new file mode 100644 index 000000000000..0c382ce171cc --- /dev/null +++ b/packages/file_picker/file_picker_web/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/file_picker/file_picker_web/README.md b/packages/file_picker/file_picker_web/README.md new file mode 100644 index 000000000000..e35424c8d320 --- /dev/null +++ b/packages/file_picker/file_picker_web/README.md @@ -0,0 +1,37 @@ +# file_picker_web + +The web implementation of [`file_picker`][1]. + +**Please set your constraint to `file_picker_web: '>=0.1.y+x <2.0.0'`** + +## Backward compatible 1.0.0 version is coming +The plugin has reached a stable API, we guarantee that version `1.0.0` will be backward compatible with `0.1.y+z`. +Please use `file_picker_web: '>=0.1.y+x <2.0.0'` as your dependency constraint to allow a smoother ecosystem migration. +For more details see: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 + +## Usage + +### Import the package +To use this plugin in your Flutter Web app, simply add it as a dependency in +your pubspec alongside the base `file_picker` plugin. + +_(This is only temporary: in the future we hope to make this package an +"endorsed" implementation of `file_picker`, so that it is automatically +included in your Flutter Web app when you depend on `package:file_picker`.)_ + +This is what the above means to your `pubspec.yaml`: + +```yaml +... +dependencies: + ... + file_picker: ^0.1.0 + file_picker_web: ^0.1.0 + ... +``` + +### Use the plugin +Once you have the `file_picker_web` dependency in your pubspec, you should +be able to use `package:file_picker` as normal. + +[1]: ../file_picker/file_picker diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart new file mode 100644 index 000000000000..36479a275b1c --- /dev/null +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -0,0 +1,20 @@ +import 'dart:async'; + +import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; + +/// The web implementation of [FilePickerPlatform]. +/// +/// This class implements the `package:file_picker` functionality for the web. +class FilePickerPlugin extends FilePickerPlatform { + /// Registers this class as the default instance of [FilePickerPlatform]. + static void registerWith(Registrar registrar) { + FilePickerPlatform.instance = FilePickerPlugin(); + } + + + @override + Future getMessage() { + return Future.value("Hello from the web implementation of file_picker!"); + } +} diff --git a/packages/file_picker/file_picker_web/pubspec.yaml b/packages/file_picker/file_picker_web/pubspec.yaml new file mode 100644 index 000000000000..57cde59325a3 --- /dev/null +++ b/packages/file_picker/file_picker_web/pubspec.yaml @@ -0,0 +1,35 @@ +name: file_picker_web +description: Web platform implementation of file_picker +homepage: https://github.com/flutter/plugins/tree/master/packages/file_picker/file_picker +# 0.1.y+z is compatible with 1.0.0, if you land a breaking change bump +# the version to 2.0.0. +# See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 +version: 0.1.0 + +flutter: + plugin: + platforms: + web: + pluginClass: FilePickerPlugin + fileName: file_picker_web.dart + +dependencies: + file_picker_platform_interface: + path: ../file_picker_platform_interface + platform_detect: ^1.4.0 + flutter: + sdk: flutter + flutter_web_plugins: + sdk: flutter + meta: ^1.1.7 + +dev_dependencies: + flutter_test: + sdk: flutter + url_launcher: ^5.2.5 + pedantic: ^1.8.0 + mockito: ^4.1.1 + +environment: + sdk: ">=2.2.0 <3.0.0" + flutter: ">=1.10.0 <2.0.0" diff --git a/packages/file_picker/file_picker_web/test/file_picker_web_test.dart b/packages/file_picker/file_picker_web/test/file_picker_web_test.dart new file mode 100644 index 000000000000..d471ab5e3446 --- /dev/null +++ b/packages/file_picker/file_picker_web/test/file_picker_web_test.dart @@ -0,0 +1,17 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@TestOn('chrome') // Uses web-only Flutter SDK + +import 'dart:html' as html; +import 'package:flutter_test/flutter_test.dart'; +import 'package:file_picker_web/file_picker_web.dart'; +import 'package:mockito/mockito.dart'; + +import 'package:platform_detect/test_utils.dart' as platform; + +class MockWindow extends Mock implements html.Window {} + +void main() { +} \ No newline at end of file From 23365e188f38db9241f6aa3a2ce2bbc188f683fa Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Thu, 16 Jul 2020 17:58:08 -0700 Subject: [PATCH 061/151] Add DOM Initialization and Test a[download] --- .../file_picker/example/lib/main.dart | 2 +- .../file_picker_web/lib/file_picker_web.dart | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index 00edac95ff8f..5616a5ebc988 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -60,7 +60,7 @@ class _MyHomePageState extends State { ), SizedBox(height: 10), RaisedButton( - child: Text('Press for a message'), + child: Text('Press for a message and a file'), onPressed: () => { _getMessage() }, ), ], diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 36479a275b1c..d69d55ef4fa3 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -1,20 +1,56 @@ import 'dart:async'; +import 'dart:html' as html; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +final String _kFilePickerInputsDomId = '__file_picker_web-file-input'; + /// The web implementation of [FilePickerPlatform]. /// /// This class implements the `package:file_picker` functionality for the web. class FilePickerPlugin extends FilePickerPlatform { + html.Element _target; + + /// Default constructor, initializes _target to a DOM element + /// that we can use to host HTML elements + FilePickerPlugin() { + _target = _ensureInitialized(_kFilePickerInputsDomId); + } + /// Registers this class as the default instance of [FilePickerPlatform]. static void registerWith(Registrar registrar) { FilePickerPlatform.instance = FilePickerPlugin(); } + /// Test download attribute. + void _downloadTest() { + html.AnchorElement element = html.AnchorElement( + href: 'data:text/plain,Hello%2C%20World!', + ); + element.download = 'TestFile'; + + _target.children.clear(); + _target.children.add(element); + element.click(); + } @override Future getMessage() { + _downloadTest(); return Future.value("Hello from the web implementation of file_picker!"); } + + /// Initializes a DOM container where we can host input elements. + html.Element _ensureInitialized(String id) { + var target = html.querySelector('#${id}'); + if (target == null) { + final html.Element targetElement = + html.Element.tag('flt-image-picker-inputs')..id = id; + + html.querySelector('body').children.add(targetElement); + target = targetElement; + } + return target; + } } From 59a3bdae4ff1f3b8b588966799bb397974a1cf66 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Sun, 19 Jul 2020 15:52:40 -0700 Subject: [PATCH 062/151] Add FilePicker Type + Add TextField to Example --- .../file_picker/example/lib/main.dart | 17 +++++ .../lib/file_picker_platform_interface.dart | 45 +----------- .../method_channel_file_picker.dart | 2 +- .../file_picker_interface.dart | 43 ++++++++++++ .../lib/src/types/camera_device.dart | 18 +++++ .../lib/src/types/image_source.dart | 12 ++++ .../lib/src/types/lost_data_response.dart | 52 ++++++++++++++ .../lib/src/types/picked_file/base.dart | 70 +++++++++++++++++++ .../lib/src/types/picked_file/html.dart | 61 ++++++++++++++++ .../lib/src/types/picked_file/io.dart | 47 +++++++++++++ .../lib/src/types/picked_file/lost_data.dart | 49 +++++++++++++ .../src/types/picked_file/picked_file.dart | 4 ++ .../src/types/picked_file/unsupported.dart | 26 +++++++ .../lib/src/types/retrieve_type.dart | 12 ++++ .../lib/src/types/types.dart | 11 +++ .../pubspec.yaml | 1 + .../test/method_channel_file_picker.dart | 3 - .../file_picker_web/lib/file_picker_web.dart | 2 +- 18 files changed, 427 insertions(+), 48 deletions(-) rename packages/file_picker/file_picker_platform_interface/lib/{ => src/method_channel}/method_channel_file_picker.dart (91%) create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/camera_device.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/image_source.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/lost_data_response.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/base.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/html.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/io.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/lost_data.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/picked_file.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/unsupported.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/retrieve_type.dart create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index 5616a5ebc988..b2f441f3e301 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -36,6 +36,13 @@ class MyHomePage extends StatefulWidget { /// State of Home Page class _MyHomePageState extends State { String _msg = ""; + final TextEditingController _controller = TextEditingController(); + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } void _getMessage() async { String msg = await getMessage(); @@ -55,6 +62,16 @@ class _MyHomePageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ + Container( + width: 150, + child: TextField( + textAlign: TextAlign.center, + controller: _controller, + decoration: InputDecoration( + hintText: 'Enter File Contents', + ), + ), + ), Text( this._msg ), diff --git a/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart index d0f557cff059..f851916ca9e4 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart @@ -1,43 +1,2 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -import 'method_channel_file_picker.dart'; - -/// 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 -/// [FilePickerPlatform] methods. -abstract class FilePickerPlatform extends PlatformInterface { - /// Constructs a FilePickerPlatform. - FilePickerPlatform() : super(token: _token); - - static final Object _token = Object(); - - static FilePickerPlatform _instance = MethodChannelFilePicker(); - - /// The default instance of [FilePickerPlatform] to use. - /// - /// Defaults to [MethodChannelFilePicker]. - static FilePickerPlatform get instance => _instance; - - /// Platform-specific plugins should set this with their own platform-specific - /// class that extends [FilePickerPlatform] when they register themselves. - static set instance(FilePickerPlatform instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - - /// Returns the message from each platform implementation - Future getMessage() { - throw UnimplementedError('getMessage() has not been implemented.'); - } -} +export 'src/platform_interface/file_picker_interface.dart'; +export 'src/types/types.dart'; diff --git a/packages/file_picker/file_picker_platform_interface/lib/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart similarity index 91% rename from packages/file_picker/file_picker_platform_interface/lib/method_channel_file_picker.dart rename to packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart index debaaab8b15e..eac349069202 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/method_channel_file_picker.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart @@ -7,7 +7,7 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:meta/meta.dart' show required; -import 'file_picker_platform_interface.dart'; +import '../platform_interface/file_picker_interface.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart new file mode 100644 index 000000000000..22a3508b4f5a --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -0,0 +1,43 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import '../method_channel/method_channel_file_picker.dart'; + +/// 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 +/// [FilePickerPlatform] methods. +abstract class FilePickerPlatform extends PlatformInterface { + /// Constructs a FilePickerPlatform. + FilePickerPlatform() : super(token: _token); + + static final Object _token = Object(); + + static FilePickerPlatform _instance = MethodChannelFilePicker(); + + /// The default instance of [FilePickerPlatform] to use. + /// + /// Defaults to [MethodChannelFilePicker]. + static FilePickerPlatform get instance => _instance; + + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [FilePickerPlatform] when they register themselves. + static set instance(FilePickerPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + + /// Returns the message from each platform implementation + Future getMessage() { + throw UnimplementedError('getMessage() has not been implemented.'); + } +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/camera_device.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/camera_device.dart new file mode 100644 index 000000000000..6c70fd451a0e --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/camera_device.dart @@ -0,0 +1,18 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Which camera to use when picking images/videos while source is `ImageSource.camera`. +/// +/// Not every device supports both of the positions. +enum CameraDevice { + /// Use the rear camera. + /// + /// In most of the cases, it is the default configuration. + rear, + + /// Use the front camera. + /// + /// Supported on all iPhones/iPads and some Android devices. + front, +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/image_source.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/image_source.dart new file mode 100644 index 000000000000..37981e3038f1 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/image_source.dart @@ -0,0 +1,12 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Specifies the source where the picked image should come from. +enum ImageSource { + /// Opens up the device camera, letting the user to take a new picture. + camera, + + /// Opens the user's photo gallery. + gallery, +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/lost_data_response.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/lost_data_response.dart new file mode 100644 index 000000000000..73014654f6e6 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/lost_data_response.dart @@ -0,0 +1,52 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:flutter/services.dart'; +import 'types.dart'; // TODO: use 'package:...' instead + +/// The response object of [ImagePicker.retrieveLostData]. +/// +/// Only applies to Android. +/// See also: +/// * [ImagePicker.retrieveLostData] for more details on retrieving lost data. +@Deprecated('Use methods that return a LostData object instead.') +class LostDataResponse { + /// Creates an instance with the given [file], [exception], and [type]. Any of + /// the params may be null, but this is never considered to be empty. + LostDataResponse({this.file, this.exception, this.type}); + + /// Initializes an instance with all member params set to null and considered + /// to be empty. + LostDataResponse.empty() + : file = null, + exception = null, + type = null, + _empty = true; + + /// Whether it is an empty response. + /// + /// An empty response should have [file], [exception] and [type] to be null. + bool get isEmpty => _empty; + + /// The file that was lost in a previous [pickImage] or [pickVideo] call due to MainActivity being destroyed. + /// + /// Can be null if [exception] exists. + final File file; + + /// The exception of the last [pickImage] or [pickVideo]. + /// + /// If the last [pickImage] or [pickVideo] threw some exception before the MainActivity destruction, this variable keeps that + /// exception. + /// You should handle this exception as if the [pickImage] or [pickVideo] got an exception when the MainActivity was not destroyed. + /// + /// Note that it is not the exception that caused the destruction of the MainActivity. + final PlatformException exception; + + /// Can either be [RetrieveType.image] or [RetrieveType.video]; + final RetrieveType type; + + bool _empty = false; +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/base.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/base.dart new file mode 100644 index 000000000000..052f870e2e04 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/base.dart @@ -0,0 +1,70 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:meta/meta.dart'; + +/// The interface for a PickedFile. +/// +/// A PickedFile is a container that wraps the path of a selected +/// file by the user and (in some platforms, like web) the bytes +/// with the contents of the file. +/// +/// This class is a very limited subset of dart:io [File], so all +/// the methods should seem familiar. +@immutable +abstract class PickedFileBase { + /// Construct a PickedFile + PickedFileBase(String path); + + /// Get the path of the picked file. + /// + /// This should only be used as a backwards-compatibility clutch + /// for mobile apps, or cosmetic reasons only (to show the user + /// the path they've picked). + /// + /// Accessing the data contained in the picked file by its path + /// is platform-dependant (and won't work on web), so use the + /// byte getters in the PickedFile instance instead. + String get path { + throw UnimplementedError('.path has not been implemented.'); + } + + /// The name of the file as it was selected by the user in their device. + /// + /// Use only for cosmetic reasons, do not try to use this as a path. + String get name { + throw UnimplementedError('.name has not been implemented.'); + } + + /// Get the length of the file. Returns a `Future` that completes with the length in bytes. + Future length() { + throw UnimplementedError('.length() has not been implemented.'); + } + + /// Synchronously read the entire file contents as a string using the given [Encoding]. + /// + /// By default, `encoding` is [utf8]. + /// + /// Throws Exception if the operation fails. + Future readAsString({Encoding encoding = utf8}) { + throw UnimplementedError('readAsString() has not been implemented.'); + } + + /// Synchronously read the entire file contents as a list of bytes. + /// + /// Throws Exception if the operation fails. + Future readAsBytes() { + throw UnimplementedError('readAsBytes() has not been implemented.'); + } + + /// Create a new independent [Stream] for the contents of this file. + /// + /// If `start` is present, the file will be read from byte-offset `start`. Otherwise from the beginning (index 0). + /// + /// If `end` is present, only up to byte-index `end` will be read. Otherwise, until end of file. + /// + /// In order to make sure that system resources are freed, the stream must be read to completion or the subscription on the stream must be cancelled. + Stream openRead([int start, int end]) { + throw UnimplementedError('openRead() has not been implemented.'); + } +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/html.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/html.dart new file mode 100644 index 000000000000..9be3c23903dd --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/html.dart @@ -0,0 +1,61 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:http/http.dart' as http show readBytes; + +import './base.dart'; + +/// A PickedFile that works on web. +/// +/// It wraps the bytes of a selected file. +class PickedFile extends PickedFileBase { + final String path; + final Uint8List _initBytes; + final int _length; + @override + final String name; + + /// Construct a PickedFile object from its ObjectUrl. + /// + /// Optionally, this can be initialized with `bytes` and `length` + /// so no http requests are performed to retrieve files later. + /// + /// `name` needs to be passed from the outside, since we only have + /// access to it while we create the ObjectUrl. + PickedFile( + this.path, { + this.name, + int length, + Uint8List bytes, + }) : _initBytes = bytes, + _length = length, + super(path); + + Future get _bytes async { + if (_initBytes != null) { + return Future.value(UnmodifiableUint8ListView(_initBytes)); + } + return http.readBytes(path); + } + + @override + Future length() async { + return _length ?? (await _bytes).length; + } + + @override + Future readAsString({Encoding encoding = utf8}) async { + return encoding.decode(await _bytes); + } + + @override + Future readAsBytes() async { + return Future.value(await _bytes); + } + + @override + Stream openRead([int start, int end]) async* { + final bytes = await _bytes; + yield bytes.sublist(start ?? 0, end ?? bytes.length); + } +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/io.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/io.dart new file mode 100644 index 000000000000..fefa87933cc0 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/io.dart @@ -0,0 +1,47 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import './base.dart'; + +/// A PickedFile backed by a dart:io File. +class PickedFile extends PickedFileBase { + final File _file; + + /// Construct a PickedFile object backed by a dart:io File. + PickedFile(String path) + : _file = File(path), + super(path); + + @override + String get path { + return _file.path; + } + + @override + String get name { + return _file.path.split(Platform.pathSeparator).last; + } + + @override + Future length() { + return _file.length(); + } + + @override + Future readAsString({Encoding encoding = utf8}) { + return _file.readAsString(encoding: encoding); + } + + @override + Future readAsBytes() { + return _file.readAsBytes(); + } + + @override + Stream openRead([int start, int end]) { + return _file + .openRead(start ?? 0, end) + .map((chunk) => Uint8List.fromList(chunk)); + } +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/lost_data.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/lost_data.dart new file mode 100644 index 000000000000..18c5eff5de7f --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/lost_data.dart @@ -0,0 +1,49 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import '../types.dart'; // TODO: use 'package:...' instead + +/// The response object of [ImagePicker.retrieveLostData]. +/// +/// Only applies to Android. +/// See also: +/// * [ImagePicker.retrieveLostData] for more details on retrieving lost data. +class LostData { + /// Creates an instance with the given [file], [exception], and [type]. Any of + /// the params may be null, but this is never considered to be empty. + LostData({this.file, this.exception, this.type}); + + /// Initializes an instance with all member params set to null and considered + /// to be empty. + LostData.empty() + : file = null, + exception = null, + type = null, + _empty = true; + + /// Whether it is an empty response. + /// + /// An empty response should have [file], [exception] and [type] to be null. + bool get isEmpty => _empty; + + /// The file that was lost in a previous [pickImage] or [pickVideo] call due to MainActivity being destroyed. + /// + /// Can be null if [exception] exists. + final PickedFile file; + + /// The exception of the last [pickImage] or [pickVideo]. + /// + /// If the last [pickImage] or [pickVideo] threw some exception before the MainActivity destruction, this variable keeps that + /// exception. + /// You should handle this exception as if the [pickImage] or [pickVideo] got an exception when the MainActivity was not destroyed. + /// + /// Note that it is not the exception that caused the destruction of the MainActivity. + final PlatformException exception; + + /// Can either be [RetrieveType.image] or [RetrieveType.video]; + final RetrieveType type; + + bool _empty = false; +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/picked_file.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/picked_file.dart new file mode 100644 index 000000000000..b2a614ccb304 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/picked_file.dart @@ -0,0 +1,4 @@ +export 'lost_data.dart'; +export 'unsupported.dart' + if (dart.library.html) 'html.dart' + if (dart.library.io) 'io.dart'; diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/unsupported.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/unsupported.dart new file mode 100644 index 000000000000..edccd9dd9d03 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/unsupported.dart @@ -0,0 +1,26 @@ +import 'dart:typed_data'; + +import './base.dart'; + +/// A PickedFile is a cross-platform, simplified File abstraction. +/// +/// It wraps the bytes of a selected file, and its (platform-dependant) path. +class PickedFile extends PickedFileBase { + /// Construct a PickedFile object from its path. + /// + /// Optionally, this can be initialized with `bytes` and `length` + /// so no http requests are performed to retrieve data later. + /// + /// `name` may be passed from the outside, for those cases where the effective + /// `path` of the file doesn't match what the user sees when selecting it + /// (like in web) + PickedFile( + String path, { + String name, + int length, + Uint8List bytes, + }) : super(path) { + throw UnimplementedError( + 'PickedFile is not available in your current platform.'); + } +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/retrieve_type.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/retrieve_type.dart new file mode 100644 index 000000000000..cc32be9711c2 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/retrieve_type.dart @@ -0,0 +1,12 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// The type of the retrieved data in a [LostDataResponse]. +enum RetrieveType { + /// A static picture. See [ImagePicker.pickImage]. + image, + + /// A video. See [ImagePicker.pickVideo]. + video +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart new file mode 100644 index 000000000000..9c44fae1aa9d --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart @@ -0,0 +1,11 @@ +export 'camera_device.dart'; +export 'image_source.dart'; +export 'lost_data_response.dart'; +export 'retrieve_type.dart'; +export 'picked_file/picked_file.dart'; + +/// Denotes that an image is being picked. +const String kTypeImage = 'image'; + +/// Denotes that a video is being picked. +const String kTypeVideo = 'video'; diff --git a/packages/file_picker/file_picker_platform_interface/pubspec.yaml b/packages/file_picker/file_picker_platform_interface/pubspec.yaml index 359de184bfc2..2cca9d6d7a04 100644 --- a/packages/file_picker/file_picker_platform_interface/pubspec.yaml +++ b/packages/file_picker/file_picker_platform_interface/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: flutter: sdk: flutter meta: ^1.0.5 + http: ^0.12.0+1 plugin_platform_interface: ^1.0.1 dev_dependencies: diff --git a/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart index cb0f11a529f9..b5670b96635e 100644 --- a/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart +++ b/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart @@ -7,8 +7,5 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:file_picker_platform_interface/method_channel_file_picker.dart'; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; - void main() { } \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index d69d55ef4fa3..a0287c6acf8a 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -28,7 +28,7 @@ class FilePickerPlugin extends FilePickerPlatform { html.AnchorElement element = html.AnchorElement( href: 'data:text/plain,Hello%2C%20World!', ); - element.download = 'TestFile'; + element.download = ''; _target.children.clear(); _target.children.add(element); From 6ea58cd08a8a47a27bf909ee85754d96d56d714e Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Sun, 19 Jul 2020 16:09:09 -0700 Subject: [PATCH 063/151] Test Blob URL Instead of Data Scheme --- .../file_picker_web/lib/file_picker_web.dart | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index a0287c6acf8a..3ff843fda388 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -25,8 +25,12 @@ class FilePickerPlugin extends FilePickerPlatform { /// Test download attribute. void _downloadTest() { + html.Blob blob = html.Blob(["Hello World from blob!"], 'text/plain'); + + String url = html.Url.createObjectUrl(blob); + html.AnchorElement element = html.AnchorElement( - href: 'data:text/plain,Hello%2C%20World!', + href: url, ); element.download = ''; @@ -35,6 +39,13 @@ class FilePickerPlugin extends FilePickerPlatform { element.click(); } + /// Web implementation of saveFile() + /// TODO: This should take input PickedFile or similar, not string + @override + Future saveFile(String file_contents) { + + } + @override Future getMessage() { _downloadTest(); From 95c97dfb1cbfa26dda6883e73d64becf986f39e6 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 22 Jul 2020 13:53:26 -0700 Subject: [PATCH 064/151] Rename PickedFile to XFile; Implement saveFile with Uint8List --- .../file_picker/example/lib/main.dart | 23 +++++++++--------- .../file_picker/lib/file_picker.dart | 8 +++++++ .../file_picker_interface.dart | 7 ++++++ .../lib/src/types/types.dart | 2 +- .../types/{picked_file => x_file}/base.dart | 13 +++++----- .../types/{picked_file => x_file}/html.dart | 9 +++---- .../src/types/{picked_file => x_file}/io.dart | 8 +++---- .../{picked_file => x_file}/lost_data.dart | 4 ++-- .../{picked_file => x_file}/unsupported.dart | 10 ++++---- .../picked_file.dart => x_file/x_file.dart} | 0 .../file_picker_web/lib/file_picker_web.dart | 24 ++++++++----------- 11 files changed, 60 insertions(+), 48 deletions(-) rename packages/file_picker/file_picker_platform_interface/lib/src/types/{picked_file => x_file}/base.dart (89%) rename packages/file_picker/file_picker_platform_interface/lib/src/types/{picked_file => x_file}/html.dart (90%) rename packages/file_picker/file_picker_platform_interface/lib/src/types/{picked_file => x_file}/io.dart (81%) rename packages/file_picker/file_picker_platform_interface/lib/src/types/{picked_file => x_file}/lost_data.dart (95%) rename packages/file_picker/file_picker_platform_interface/lib/src/types/{picked_file => x_file}/unsupported.dart (70%) rename packages/file_picker/file_picker_platform_interface/lib/src/types/{picked_file/picked_file.dart => x_file/x_file.dart} (100%) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index b2f441f3e301..5b0cdb12d624 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -1,6 +1,10 @@ +import 'dart:typed_data'; + import 'package:flutter/material.dart'; import 'package:file_picker/file_picker.dart'; + + void main() { runApp(MyApp()); } @@ -35,7 +39,6 @@ class MyHomePage extends StatefulWidget { /// State of Home Page class _MyHomePageState extends State { - String _msg = ""; final TextEditingController _controller = TextEditingController(); @override @@ -44,11 +47,12 @@ class _MyHomePageState extends State { super.dispose(); } - void _getMessage() async { - String msg = await getMessage(); - setState(() { - this._msg = "Here is your message: " + msg; - }); + void _saveFile() async { + Uint8List data; + data = Uint8List.fromList(_controller.text.codeUnits); + + // await? + saveFile(data); } @override @@ -72,13 +76,10 @@ class _MyHomePageState extends State { ), ), ), - Text( - this._msg - ), SizedBox(height: 10), RaisedButton( - child: Text('Press for a message and a file'), - onPressed: () => { _getMessage() }, + child: Text('Press to save file'), + onPressed: () => { _saveFile() }, ), ], ), diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index 2b712eb127c7..d85eb442f24e 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -3,12 +3,20 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:typed_data'; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; +export 'package:file_picker_platform_interface/file_picker_platform_interface.dart' + show XFile; + /// Gets message from platform implementation Future getMessage() async { final String result = await FilePickerPlatform.instance.getMessage(); return result; } +/// Saves File to user's file system +void saveFile(Uint8List data) async { + return FilePickerPlatform.instance.saveFile(data); +} \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index 22a3508b4f5a..653b6f458fe3 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:typed_data'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; @@ -40,4 +41,10 @@ abstract class FilePickerPlatform extends PlatformInterface { Future getMessage() { throw UnimplementedError('getMessage() has not been implemented.'); } + + /// Saves the file to user's Disk + /// TODO: Parameters should not be string + void saveFile(Uint8List data) async { + throw UnimplementedError('saveFile() has not been implemented.'); + } } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart index 9c44fae1aa9d..9b275ed22e97 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart @@ -2,7 +2,7 @@ export 'camera_device.dart'; export 'image_source.dart'; export 'lost_data_response.dart'; export 'retrieve_type.dart'; -export 'picked_file/picked_file.dart'; +export 'x_file/x_file.dart'; /// Denotes that an image is being picked. const String kTypeImage = 'image'; diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/base.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart similarity index 89% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/base.dart rename to packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart index 052f870e2e04..52b6015a9ada 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/base.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart @@ -3,18 +3,17 @@ import 'dart:typed_data'; import 'package:meta/meta.dart'; -/// The interface for a PickedFile. +/// The interface for a XFile. /// -/// A PickedFile is a container that wraps the path of a selected +/// A XFile is a container that wraps the path of a selected /// file by the user and (in some platforms, like web) the bytes /// with the contents of the file. /// /// This class is a very limited subset of dart:io [File], so all /// the methods should seem familiar. -@immutable -abstract class PickedFileBase { - /// Construct a PickedFile - PickedFileBase(String path); +abstract class XFileBase { + /// Construct a XFile + XFileBase(String path); /// Get the path of the picked file. /// @@ -24,7 +23,7 @@ abstract class PickedFileBase { /// /// Accessing the data contained in the picked file by its path /// is platform-dependant (and won't work on web), so use the - /// byte getters in the PickedFile instance instead. + /// byte getters in the XFile instance instead. String get path { throw UnimplementedError('.path has not been implemented.'); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/html.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart similarity index 90% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/html.dart rename to packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart index 9be3c23903dd..e38cff9faf55 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/html.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart @@ -1,28 +1,29 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'dart:html'; import 'package:http/http.dart' as http show readBytes; import './base.dart'; -/// A PickedFile that works on web. +/// A XFile that works on web. /// /// It wraps the bytes of a selected file. -class PickedFile extends PickedFileBase { +class XFile extends XFileBase { final String path; final Uint8List _initBytes; final int _length; @override final String name; - /// Construct a PickedFile object from its ObjectUrl. + /// Construct a XFile object from its ObjectUrl. /// /// Optionally, this can be initialized with `bytes` and `length` /// so no http requests are performed to retrieve files later. /// /// `name` needs to be passed from the outside, since we only have /// access to it while we create the ObjectUrl. - PickedFile( + XFile( this.path, { this.name, int length, diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/io.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart similarity index 81% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/io.dart rename to packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart index fefa87933cc0..3ea492748bd1 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/io.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart @@ -4,12 +4,12 @@ import 'dart:typed_data'; import './base.dart'; -/// A PickedFile backed by a dart:io File. -class PickedFile extends PickedFileBase { +/// A XFile backed by a dart:io File. +class XFile extends XFileBase { final File _file; - /// Construct a PickedFile object backed by a dart:io File. - PickedFile(String path) + /// Construct a XFile object backed by a dart:io File. + XFile(String path) : _file = File(path), super(path); diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/lost_data.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/lost_data.dart similarity index 95% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/lost_data.dart rename to packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/lost_data.dart index 18c5eff5de7f..15bb236caece 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/lost_data.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/lost_data.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. import 'package:flutter/services.dart'; -import '../types.dart'; // TODO: use 'package:...' instead +import '../types.dart'; /// The response object of [ImagePicker.retrieveLostData]. /// @@ -31,7 +31,7 @@ class LostData { /// The file that was lost in a previous [pickImage] or [pickVideo] call due to MainActivity being destroyed. /// /// Can be null if [exception] exists. - final PickedFile file; + final XFile file; /// The exception of the last [pickImage] or [pickVideo]. /// diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/unsupported.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart similarity index 70% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/unsupported.dart rename to packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart index edccd9dd9d03..cf384f5d939b 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/unsupported.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart @@ -2,11 +2,11 @@ import 'dart:typed_data'; import './base.dart'; -/// A PickedFile is a cross-platform, simplified File abstraction. +/// A XFile is a cross-platform, simplified File abstraction. /// /// It wraps the bytes of a selected file, and its (platform-dependant) path. -class PickedFile extends PickedFileBase { - /// Construct a PickedFile object from its path. +class XFile extends XFileBase { + /// Construct a XFile object from its path. /// /// Optionally, this can be initialized with `bytes` and `length` /// so no http requests are performed to retrieve data later. @@ -14,13 +14,13 @@ class PickedFile extends PickedFileBase { /// `name` may be passed from the outside, for those cases where the effective /// `path` of the file doesn't match what the user sees when selecting it /// (like in web) - PickedFile( + XFile( String path, { String name, int length, Uint8List bytes, }) : super(path) { throw UnimplementedError( - 'PickedFile is not available in your current platform.'); + 'XFile is not available in your current platform.'); } } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/picked_file.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/picked_file/picked_file.dart rename to packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 3ff843fda388..32b6eec30122 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:html' as html; +import 'dart:typed_data'; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; @@ -23,32 +24,27 @@ class FilePickerPlugin extends FilePickerPlatform { FilePickerPlatform.instance = FilePickerPlugin(); } - /// Test download attribute. - void _downloadTest() { - html.Blob blob = html.Blob(["Hello World from blob!"], 'text/plain'); - + /// Web implementation of saveFile() + /// TODO: This should take input PickedFile or similar, not string + @override + void saveFile(Uint8List data, {String suggestedName = ''}) async { + // Create blob from data + // TODO: Handle different types + html.Blob blob = html.Blob([data], 'text/plain'); String url = html.Url.createObjectUrl(blob); + // Create an tag with the appropriate download attributes and click it html.AnchorElement element = html.AnchorElement( href: url, ); - element.download = ''; - + element.download = suggestedName; _target.children.clear(); _target.children.add(element); element.click(); } - /// Web implementation of saveFile() - /// TODO: This should take input PickedFile or similar, not string - @override - Future saveFile(String file_contents) { - - } - @override Future getMessage() { - _downloadTest(); return Future.value("Hello from the web implementation of file_picker!"); } From 1cbde02f0d4be784fd72f8991b56cab3c8839486 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 22 Jul 2020 14:59:43 -0700 Subject: [PATCH 065/151] PoC loadFile text only --- .../file_picker/example/lib/main.dart | 12 +++++++ .../file_picker/lib/file_picker.dart | 5 +++ .../file_picker_interface.dart | 7 +++- .../file_picker_web/lib/file_picker_web.dart | 34 ++++++++++++++++++- 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index 5b0cdb12d624..f6ed81a76b18 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -55,6 +55,14 @@ class _MyHomePageState extends State { saveFile(data); } + void _loadFile() async { + XFile file = await loadFile(); + + String text = await file.readAsString(); + + _controller.text = text; + } + @override Widget build(BuildContext context) { @@ -81,6 +89,10 @@ class _MyHomePageState extends State { child: Text('Press to save file'), onPressed: () => { _saveFile() }, ), + RaisedButton( + child: Text('Press to load a file'), + onPressed: () => { _loadFile() }, + ), ], ), ), diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index d85eb442f24e..b118e460e892 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -19,4 +19,9 @@ Future getMessage() async { /// Saves File to user's file system void saveFile(Uint8List data) async { return FilePickerPlatform.instance.saveFile(data); +} + +/// Loads File from user's file system +Future loadFile() { + return FilePickerPlatform.instance.loadFile(); } \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index 653b6f458fe3..b0c6d83cb2c8 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'dart:typed_data'; +import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import '../method_channel/method_channel_file_picker.dart'; @@ -42,8 +43,12 @@ abstract class FilePickerPlatform extends PlatformInterface { throw UnimplementedError('getMessage() has not been implemented.'); } + /// Load file from user's computer and return it as an XFile + Future loadFile() { + throw UnimplementedError('loadFile() has not been implemented.'); + } + /// Saves the file to user's Disk - /// TODO: Parameters should not be string void saveFile(Uint8List data) async { throw UnimplementedError('saveFile() has not been implemented.'); } diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 32b6eec30122..713c6f2ebb8f 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -24,8 +24,39 @@ class FilePickerPlugin extends FilePickerPlatform { FilePickerPlatform.instance = FilePickerPlugin(); } + /// Load file from user's computer and return it as an XFile + /// TODO: multiple files + Future loadFile() { + // Create a file input element + html.FileUploadInputElement element = html.FileUploadInputElement(); + element.accept = 'text/plain'; // TODO: accept different types + element.multiple = true; + + // Add the file input element and click it + _target.children.clear(); + _target.children.add(element); + element.click(); + + Completer _completer = Completer(); + + // Get the returned files + // TODO: Handle errors + element.onChange.first.then((event) { + // TODO: Multiple files + html.File files = element.files.first; + String url = html.Url.createObjectUrl(files); + String name = files.name; + int length = files.size; + + XFile loadedFile = XFile(url, name: name, length: length); + + _completer.complete(loadedFile); + }); + + return _completer.future; + } + /// Web implementation of saveFile() - /// TODO: This should take input PickedFile or similar, not string @override void saveFile(Uint8List data, {String suggestedName = ''}) async { // Create blob from data @@ -43,6 +74,7 @@ class FilePickerPlugin extends FilePickerPlatform { element.click(); } + /// "Hello World" function for testing @override Future getMessage() { return Future.value("Hello from the web implementation of file_picker!"); From 312d53f33a47222598ee5ebe1683050c56d6ade2 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 22 Jul 2020 16:56:59 -0700 Subject: [PATCH 066/151] Add suggestedName to saveFile --- .../file_picker/example/lib/main.dart | 29 ++++++++++++++----- .../file_picker/lib/file_picker.dart | 4 +-- .../file_picker_interface.dart | 2 +- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index f6ed81a76b18..c5d58fe939a6 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -39,20 +39,25 @@ class MyHomePage extends StatefulWidget { /// State of Home Page class _MyHomePageState extends State { - final TextEditingController _controller = TextEditingController(); + final TextEditingController _fileController = TextEditingController(); + final TextEditingController _nameController = TextEditingController(); @override void dispose() { - _controller.dispose(); + _fileController.dispose(); super.dispose(); } void _saveFile() async { Uint8List data; - data = Uint8List.fromList(_controller.text.codeUnits); + data = Uint8List.fromList(_fileController.text.codeUnits); // await? - saveFile(data); + if (_nameController.text == '') { + saveFile(data); + } else { + saveFile(data, suggestedName: _nameController.text); + } } void _loadFile() async { @@ -60,7 +65,7 @@ class _MyHomePageState extends State { String text = await file.readAsString(); - _controller.text = text; + _fileController.text = text; } @override @@ -75,10 +80,20 @@ class _MyHomePageState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Container( - width: 150, + width: 300, child: TextField( textAlign: TextAlign.center, - controller: _controller, + controller: _nameController, + decoration: InputDecoration( + hintText: '(Optional) Suggest File Name', + ), + ), + ), + Container( + width: 300, + child: TextField( + textAlign: TextAlign.center, + controller: _fileController, decoration: InputDecoration( hintText: 'Enter File Contents', ), diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index b118e460e892..4a521333e812 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -17,8 +17,8 @@ Future getMessage() async { } /// Saves File to user's file system -void saveFile(Uint8List data) async { - return FilePickerPlatform.instance.saveFile(data); +void saveFile(Uint8List data, {String suggestedName}) async { + return FilePickerPlatform.instance.saveFile(data, suggestedName: suggestedName); } /// Loads File from user's file system diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index b0c6d83cb2c8..4cadf7609a93 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -49,7 +49,7 @@ abstract class FilePickerPlatform extends PlatformInterface { } /// Saves the file to user's Disk - void saveFile(Uint8List data) async { + void saveFile(Uint8List data, {String suggestedName}) async { throw UnimplementedError('saveFile() has not been implemented.'); } } From 58ae8a541b023355b5efcdf2622c742af925c978 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 22 Jul 2020 17:02:48 -0700 Subject: [PATCH 067/151] Use file name in loadFile example --- packages/file_picker/file_picker/example/lib/main.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index c5d58fe939a6..5108ce4bac16 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -66,6 +66,10 @@ class _MyHomePageState extends State { String text = await file.readAsString(); _fileController.text = text; + + if (file.name != '') { + _nameController.text = file.name; + } } @override From ba80aae9bfda2c99b3f4ae895b6646295a0d7982 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Thu, 23 Jul 2020 17:03:15 -0700 Subject: [PATCH 068/151] Load Multiple Files, Save As a Type --- .../file_picker/example/lib/main.dart | 32 ++++++++---- .../file_picker/lib/file_picker.dart | 14 ++---- .../file_picker_interface.dart | 10 +--- .../file_picker_web/lib/file_picker_web.dart | 50 ++++++++++++------- 4 files changed, 61 insertions(+), 45 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index 5108ce4bac16..c0aa37d24023 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -19,7 +19,7 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: MyHomePage(title: 'File Picker Demo Home Page'), + home: MyHomePage(title: 'File Selector Demo Home Page'), ); } } @@ -41,6 +41,7 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { final TextEditingController _fileController = TextEditingController(); final TextEditingController _nameController = TextEditingController(); + final TextEditingController _extensionController = TextEditingController(); @override void dispose() { @@ -54,21 +55,26 @@ class _MyHomePageState extends State { // await? if (_nameController.text == '') { - saveFile(data); + saveFile(data, type: 'text/plain'); } else { - saveFile(data, suggestedName: _nameController.text); + saveFile(data, type: 'text/plain', suggestedName: _nameController.text); } } void _loadFile() async { - XFile file = await loadFile(); + List file; + if (_extensionController.text.isNotEmpty) { + file = await loadFile(acceptedTypes: _extensionController.text.split(',')); + } else { + file = await loadFile(); + } - String text = await file.readAsString(); + String text = await file.first.readAsString(); _fileController.text = text; - if (file.name != '') { - _nameController.text = file.name; + if (file.first.name.isNotEmpty) { + _nameController.text = file.first.name; } } @@ -86,7 +92,6 @@ class _MyHomePageState extends State { Container( width: 300, child: TextField( - textAlign: TextAlign.center, controller: _nameController, decoration: InputDecoration( hintText: '(Optional) Suggest File Name', @@ -96,7 +101,7 @@ class _MyHomePageState extends State { Container( width: 300, child: TextField( - textAlign: TextAlign.center, + maxLines: null, controller: _fileController, decoration: InputDecoration( hintText: 'Enter File Contents', @@ -108,6 +113,15 @@ class _MyHomePageState extends State { child: Text('Press to save file'), onPressed: () => { _saveFile() }, ), + Container( + width: 300, + child: TextField( + controller: _extensionController, + decoration: InputDecoration( + hintText: '(Optional) Accepted Load Extensions', + ), + ), + ), RaisedButton( child: Text('Press to load a file'), onPressed: () => { _loadFile() }, diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index 4a521333e812..e41862beded3 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -10,18 +10,12 @@ import 'package:file_picker_platform_interface/file_picker_platform_interface.da export 'package:file_picker_platform_interface/file_picker_platform_interface.dart' show XFile; -/// Gets message from platform implementation -Future getMessage() async { - final String result = await FilePickerPlatform.instance.getMessage(); - return result; -} - /// Saves File to user's file system -void saveFile(Uint8List data, {String suggestedName}) async { - return FilePickerPlatform.instance.saveFile(data, suggestedName: suggestedName); +void saveFile(Uint8List data, {String type = '', String suggestedName}) async { + return FilePickerPlatform.instance.saveFile(data, type: type, suggestedName: suggestedName); } /// Loads File from user's file system -Future loadFile() { - return FilePickerPlatform.instance.loadFile(); +Future> loadFile({List acceptedTypes}) { + return FilePickerPlatform.instance.loadFile(acceptedTypes: acceptedTypes); } \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index 4cadf7609a93..e089870b1741 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -37,19 +37,13 @@ abstract class FilePickerPlatform extends PlatformInterface { _instance = instance; } - - /// Returns the message from each platform implementation - Future getMessage() { - throw UnimplementedError('getMessage() has not been implemented.'); - } - /// Load file from user's computer and return it as an XFile - Future loadFile() { + Future> loadFile({List acceptedTypes}) { throw UnimplementedError('loadFile() has not been implemented.'); } /// Saves the file to user's Disk - void saveFile(Uint8List data, {String suggestedName}) async { + void saveFile(Uint8List data, {String type, String suggestedName}) async { throw UnimplementedError('saveFile() has not been implemented.'); } } diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 713c6f2ebb8f..9321d3c5a2e4 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -26,10 +26,20 @@ class FilePickerPlugin extends FilePickerPlatform { /// Load file from user's computer and return it as an XFile /// TODO: multiple files - Future loadFile() { + Future> loadFile({List acceptedTypes}) { + String inputString = ''; + for (String element in acceptedTypes) { + if (inputString.isNotEmpty) { + inputString += ','; + } + inputString += element; + } + // Create a file input element html.FileUploadInputElement element = html.FileUploadInputElement(); - element.accept = 'text/plain'; // TODO: accept different types + if (inputString.isNotEmpty) { + element.accept = inputString; + } element.multiple = true; // Add the file input element and click it @@ -37,20 +47,25 @@ class FilePickerPlugin extends FilePickerPlatform { _target.children.add(element); element.click(); - Completer _completer = Completer(); + Completer> _completer = Completer>(); // Get the returned files // TODO: Handle errors element.onChange.first.then((event) { // TODO: Multiple files - html.File files = element.files.first; - String url = html.Url.createObjectUrl(files); - String name = files.name; - int length = files.size; - - XFile loadedFile = XFile(url, name: name, length: length); + List files = element.files; + List returnFiles = List(); + + for (html.File file in files) { + String url = html.Url.createObjectUrl(file); + String name = file.name; + int length = file.size; + + returnFiles.add(new XFile(url, name: name, length: length)); + } + - _completer.complete(loadedFile); + _completer.complete(returnFiles); }); return _completer.future; @@ -58,10 +73,15 @@ class FilePickerPlugin extends FilePickerPlatform { /// Web implementation of saveFile() @override - void saveFile(Uint8List data, {String suggestedName = ''}) async { + void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { // Create blob from data // TODO: Handle different types - html.Blob blob = html.Blob([data], 'text/plain'); + html.Blob blob; + if(type.isEmpty) { + blob = html.Blob([data]); + } else { + blob = html.Blob([data], type); + } String url = html.Url.createObjectUrl(blob); // Create an tag with the appropriate download attributes and click it @@ -74,12 +94,6 @@ class FilePickerPlugin extends FilePickerPlatform { element.click(); } - /// "Hello World" function for testing - @override - Future getMessage() { - return Future.value("Hello from the web implementation of file_picker!"); - } - /// Initializes a DOM container where we can host input elements. html.Element _ensureInitialized(String id) { var target = html.querySelector('#${id}'); From 1b14104666fbc553040035ff45cfeb9f4ba0074f Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 27 Jul 2020 14:28:55 -0700 Subject: [PATCH 069/151] Add Method Channel Implementation + Some Refactor --- .../method_channel_file_picker.dart | 25 ++++++++++++++++--- .../lib/src/types/x_file/base.dart | 2 -- .../lib/src/types/x_file/html.dart | 1 - .../file_picker_web/lib/file_picker_web.dart | 2 +- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart index eac349069202..89a33a18d0d9 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart @@ -2,20 +2,37 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; +import 'dart:typed_data'; import 'package:flutter/services.dart'; -import 'package:meta/meta.dart' show required; import '../platform_interface/file_picker_interface.dart'; +import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); /// An implementation of [FilePickerPlatform] that uses method channels. class MethodChannelFilePicker extends FilePickerPlatform { + /// Load file from user's computer and return it as an XFile + @override + Future> loadFile({List acceptedTypes}) { + return _channel.invokeMethod>( + 'loadFile', + { + 'acceptedTypes': acceptedTypes, + }, + ); + } + /// Saves the file to user's Disk @override - Future getMessage() { - return _channel.invokeMethod('getMessage'); + void saveFile(Uint8List data, {String type, String suggestedName}) async { + await _channel.invokeMethod( + 'saveFile', + { + 'type': type, + 'suggestedName': suggestedName, + }, + ); } } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart index 52b6015a9ada..4d12145ce14b 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart @@ -1,8 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:meta/meta.dart'; - /// The interface for a XFile. /// /// A XFile is a container that wraps the path of a selected diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart index e38cff9faf55..e5ecd11fee38 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart @@ -1,6 +1,5 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'dart:html'; import 'package:http/http.dart' as http show readBytes; diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 9321d3c5a2e4..2f058da59c26 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -61,7 +61,7 @@ class FilePickerPlugin extends FilePickerPlatform { String name = file.name; int length = file.size; - returnFiles.add(new XFile(url, name: name, length: length)); + returnFiles.add(XFile(url, name: name, length: length)); } From 5880089b5a000530a80cd1981dfb328611a9bd83 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 27 Jul 2020 15:01:25 -0700 Subject: [PATCH 070/151] Initial Refactor Change some variables to final, condense for loops and some if statements. --- .../file_picker_web/lib/file_picker_web.dart | 51 ++++++++----------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 2f058da59c26..281e2b49ed15 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'dart:html' as html; +import 'dart:html'; import 'dart:typed_data'; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; @@ -11,7 +11,7 @@ final String _kFilePickerInputsDomId = '__file_picker_web-file-input'; /// /// This class implements the `package:file_picker` functionality for the web. class FilePickerPlugin extends FilePickerPlatform { - html.Element _target; + Element _target; /// Default constructor, initializes _target to a DOM element /// that we can use to host HTML elements @@ -27,18 +27,12 @@ class FilePickerPlugin extends FilePickerPlatform { /// Load file from user's computer and return it as an XFile /// TODO: multiple files Future> loadFile({List acceptedTypes}) { - String inputString = ''; - for (String element in acceptedTypes) { - if (inputString.isNotEmpty) { - inputString += ','; - } - inputString += element; - } + String acceptedTypeString = acceptedTypes.where((e) => e.isNotEmpty).join(','); // Create a file input element - html.FileUploadInputElement element = html.FileUploadInputElement(); - if (inputString.isNotEmpty) { - element.accept = inputString; + final FileUploadInputElement element = FileUploadInputElement(); + if (acceptedTypeString.isNotEmpty) { + element.accept = acceptedTypeString; } element.multiple = true; @@ -47,17 +41,17 @@ class FilePickerPlugin extends FilePickerPlatform { _target.children.add(element); element.click(); - Completer> _completer = Completer>(); + final Completer> _completer = Completer(); // Get the returned files // TODO: Handle errors element.onChange.first.then((event) { - // TODO: Multiple files - List files = element.files; + // File type from dart:html class + List files = element.files; List returnFiles = List(); - for (html.File file in files) { - String url = html.Url.createObjectUrl(file); + for (File file in files) { + String url = Url.createObjectUrl(file); String name = file.name; int length = file.size; @@ -76,16 +70,13 @@ class FilePickerPlugin extends FilePickerPlatform { void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { // Create blob from data // TODO: Handle different types - html.Blob blob; - if(type.isEmpty) { - blob = html.Blob([data]); - } else { - blob = html.Blob([data], type); - } - String url = html.Url.createObjectUrl(blob); + + final Blob blob = type.isEmpty ? Blob([data]) : Blob([data]); + + String url = Url.createObjectUrl(blob); // Create an tag with the appropriate download attributes and click it - html.AnchorElement element = html.AnchorElement( + final AnchorElement element = AnchorElement( href: url, ); element.download = suggestedName; @@ -95,13 +86,13 @@ class FilePickerPlugin extends FilePickerPlatform { } /// Initializes a DOM container where we can host input elements. - html.Element _ensureInitialized(String id) { - var target = html.querySelector('#${id}'); + Element _ensureInitialized(String id) { + var target = querySelector('#${id}'); if (target == null) { - final html.Element targetElement = - html.Element.tag('flt-image-picker-inputs')..id = id; + final Element targetElement = + Element.tag('flt-image-picker-inputs')..id = id; - html.querySelector('body').children.add(targetElement); + querySelector('body').children.add(targetElement); target = targetElement; } return target; From d29afa29a5e62da510650365a1db2ece7a37d171 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 27 Jul 2020 16:54:57 -0700 Subject: [PATCH 071/151] Basic Method Channel Tests --- .../pubspec.yaml | 1 + .../test/method_channel_file_picker.dart | 11 ----- .../test/method_channel_file_picker_test.dart | 47 +++++++++++++++++++ .../file_picker_web/lib/file_picker_web.dart | 2 +- 4 files changed, 49 insertions(+), 12 deletions(-) delete mode 100644 packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart create mode 100644 packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker_test.dart diff --git a/packages/file_picker/file_picker_platform_interface/pubspec.yaml b/packages/file_picker/file_picker_platform_interface/pubspec.yaml index 2cca9d6d7a04..249d9f1f9a89 100644 --- a/packages/file_picker/file_picker_platform_interface/pubspec.yaml +++ b/packages/file_picker/file_picker_platform_interface/pubspec.yaml @@ -13,6 +13,7 @@ dependencies: plugin_platform_interface: ^1.0.1 dev_dependencies: + test: ^1.15.0 flutter_test: sdk: flutter mockito: ^4.1.1 diff --git a/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart deleted file mode 100644 index b5670b96635e..000000000000 --- a/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker.dart +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:mockito/mockito.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -void main() { -} \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker_test.dart b/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker_test.dart new file mode 100644 index 000000000000..fd5dd8deea2a --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker_test.dart @@ -0,0 +1,47 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:mockito/mockito.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; +import 'package:file_picker_platform_interface/src/method_channel/method_channel_file_picker.dart'; + +void main() { + group('$FilePickerPlatform', () { + test('$MethodChannelFilePicker() is the default instance', () { + expect(FilePickerPlatform.instance, + isInstanceOf()); + }); + + test('Cannot be implemented with `implements`', () { + expect(() { + FilePickerPlatform.instance = ImplementsFilePickerPlatform(); + }, throwsA(isInstanceOf())); + }); + + test('Can be mocked with `implements`', () { + final FilePickerPlatformMock mock = FilePickerPlatformMock(); + FilePickerPlatform.instance = mock; + }); + + test('Can be extended', () { + FilePickerPlatform.instance = ExtendsFilePickerPlatform(); + }); + }); + + +} + + +class FilePickerPlatformMock extends Mock + with MockPlatformInterfaceMixin + implements FilePickerPlatform {} + +class ImplementsFilePickerPlatform extends Mock + implements FilePickerPlatform {} + +class ExtendsFilePickerPlatform extends FilePickerPlatform {} \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 281e2b49ed15..497dafb85d8c 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -90,7 +90,7 @@ class FilePickerPlugin extends FilePickerPlatform { var target = querySelector('#${id}'); if (target == null) { final Element targetElement = - Element.tag('flt-image-picker-inputs')..id = id; + Element.tag('flt-file-picker-inputs')..id = id; querySelector('body').children.add(targetElement); target = targetElement; From 49fe0b8965cd44c23a6740d9ddb0a7c017a44f09 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 27 Jul 2020 17:23:34 -0700 Subject: [PATCH 072/151] Break Down loadFile Into Smaller Parts --- .../file_picker_web/lib/file_picker_web.dart | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 497dafb85d8c..83421736d12c 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -24,22 +24,30 @@ class FilePickerPlugin extends FilePickerPlatform { FilePickerPlatform.instance = FilePickerPlugin(); } - /// Load file from user's computer and return it as an XFile - /// TODO: multiple files - Future> loadFile({List acceptedTypes}) { - String acceptedTypeString = acceptedTypes.where((e) => e.isNotEmpty).join(','); - - // Create a file input element + FileUploadInputElement _createFileInputElement(String accepted) { final FileUploadInputElement element = FileUploadInputElement(); - if (acceptedTypeString.isNotEmpty) { - element.accept = acceptedTypeString; + if (accepted.isNotEmpty) { + element.accept = accepted; } element.multiple = true; + return element; + } + + void _addElementToDomAndClick(Element element) { // Add the file input element and click it _target.children.clear(); _target.children.add(element); element.click(); + } + + /// Load file from user's computer and return it as an XFile + Future> loadFile({List acceptedTypes = const []}) { + String acceptedTypeString = acceptedTypes?.where((e) => e.isNotEmpty)?.join(',') ?? ''; + + final FileUploadInputElement element = _createFileInputElement(acceptedTypeString); + + _addElementToDomAndClick(element); final Completer> _completer = Completer(); @@ -58,7 +66,6 @@ class FilePickerPlugin extends FilePickerPlatform { returnFiles.add(XFile(url, name: name, length: length)); } - _completer.complete(returnFiles); }); @@ -71,18 +78,15 @@ class FilePickerPlugin extends FilePickerPlatform { // Create blob from data // TODO: Handle different types - final Blob blob = type.isEmpty ? Blob([data]) : Blob([data]); + final Blob blob = type.isEmpty ? Blob([data]) : Blob([data], type); String url = Url.createObjectUrl(blob); // Create an tag with the appropriate download attributes and click it - final AnchorElement element = AnchorElement( - href: url, - ); + final AnchorElement element = AnchorElement(href: url); element.download = suggestedName; - _target.children.clear(); - _target.children.add(element); - element.click(); + + _addElementToDomAndClick(element); } /// Initializes a DOM container where we can host input elements. From 5d6bec0e3b08765f8a10fd1e0207b2df3aaba2cc Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 28 Jul 2020 14:14:29 -0700 Subject: [PATCH 073/151] Use FileTypeFilterGroup class --- .../file_picker/example/lib/main.dart | 6 ++-- .../file_picker/lib/file_picker.dart | 4 +-- .../method_channel_file_picker.dart | 2 +- .../file_picker_interface.dart | 2 +- .../src/types/filter_group/filter_group.dart | 28 +++++++++++++++++++ .../lib/src/types/types.dart | 2 ++ .../file_picker_web/lib/file_picker_web.dart | 10 +++++-- 7 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index c0aa37d24023..5f4ab99e2ef6 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -3,8 +3,6 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:file_picker/file_picker.dart'; - - void main() { runApp(MyApp()); } @@ -64,7 +62,9 @@ class _MyHomePageState extends State { void _loadFile() async { List file; if (_extensionController.text.isNotEmpty) { - file = await loadFile(acceptedTypes: _extensionController.text.split(',')); + List type = List(); + type.add(FileTypeFilterGroup(label: 'Example Files', fileExtensions: _extensionController.text.split(','))); + file = await loadFile(acceptedTypes: type); } else { file = await loadFile(); } diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index e41862beded3..5e10892fe44b 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -8,7 +8,7 @@ import 'dart:typed_data'; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; export 'package:file_picker_platform_interface/file_picker_platform_interface.dart' - show XFile; + show XFile, FileTypeFilterGroup; /// Saves File to user's file system void saveFile(Uint8List data, {String type = '', String suggestedName}) async { @@ -16,6 +16,6 @@ void saveFile(Uint8List data, {String type = '', String suggestedName}) async { } /// Loads File from user's file system -Future> loadFile({List acceptedTypes}) { +Future> loadFile({List acceptedTypes}) { return FilePickerPlatform.instance.loadFile(acceptedTypes: acceptedTypes); } \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart index 89a33a18d0d9..3e90f4dad471 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart @@ -15,7 +15,7 @@ const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); class MethodChannelFilePicker extends FilePickerPlatform { /// Load file from user's computer and return it as an XFile @override - Future> loadFile({List acceptedTypes}) { + Future> loadFile({List acceptedTypes}) { return _channel.invokeMethod>( 'loadFile', { diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index e089870b1741..cfe5a437ea29 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -38,7 +38,7 @@ abstract class FilePickerPlatform extends PlatformInterface { } /// Load file from user's computer and return it as an XFile - Future> loadFile({List acceptedTypes}) { + Future> loadFile({List acceptedTypes}) { throw UnimplementedError('loadFile() has not been implemented.'); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart new file mode 100644 index 000000000000..186e1eca42b3 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart @@ -0,0 +1,28 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// A set of allowed file types. +class FileTypeFilterGroup { + /// Creates a new group with the given label and file extensions. + const FileTypeFilterGroup({this.label, this.fileExtensions}); + + /// The label for the grouping. On platforms that support selectable groups, + /// this will be visible to the user for selecting the group. + final String label; + + /// A list of allowed file extensions. E.g., ['png', 'jpg', 'jpeg', 'gif']. + /// + /// A null or empty list indicates any type is allowed. + final List fileExtensions; +} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart index 9b275ed22e97..24e7e7ff83dd 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart @@ -4,6 +4,8 @@ export 'lost_data_response.dart'; export 'retrieve_type.dart'; export 'x_file/x_file.dart'; +export 'filter_group/filter_group.dart'; + /// Denotes that an image is being picked. const String kTypeImage = 'image'; diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 83421736d12c..c1db2ce309db 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -42,15 +42,19 @@ class FilePickerPlugin extends FilePickerPlatform { } /// Load file from user's computer and return it as an XFile - Future> loadFile({List acceptedTypes = const []}) { - String acceptedTypeString = acceptedTypes?.where((e) => e.isNotEmpty)?.join(',') ?? ''; + Future> loadFile({List acceptedTypes = const []}) { + List allExtensions = List(); + for (FileTypeFilterGroup group in acceptedTypes) { + allExtensions += group.fileExtensions; + } + String acceptedTypeString = allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; final FileUploadInputElement element = _createFileInputElement(acceptedTypeString); _addElementToDomAndClick(element); final Completer> _completer = Completer(); - + // Get the returned files // TODO: Handle errors element.onChange.first.then((event) { From 1a8a9281bb3c44bde005b604f332fb8a0948651e Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 28 Jul 2020 16:55:23 -0700 Subject: [PATCH 074/151] Bug Fix (No Input to File Extension) --- ...est.dart => file_picker_platform_interface_test.dart} | 0 .../file_picker/file_picker_web/lib/file_picker_web.dart | 4 ++-- .../file_picker_web/test/file_picker_web_test.dart | 9 +++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) rename packages/file_picker/file_picker_platform_interface/test/{method_channel_file_picker_test.dart => file_picker_platform_interface_test.dart} (100%) diff --git a/packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker_test.dart b/packages/file_picker/file_picker_platform_interface/test/file_picker_platform_interface_test.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/test/method_channel_file_picker_test.dart rename to packages/file_picker/file_picker_platform_interface/test/file_picker_platform_interface_test.dart diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index c1db2ce309db..5dea1ad162ab 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -42,9 +42,9 @@ class FilePickerPlugin extends FilePickerPlatform { } /// Load file from user's computer and return it as an XFile - Future> loadFile({List acceptedTypes = const []}) { + Future> loadFile({List acceptedTypes}) { List allExtensions = List(); - for (FileTypeFilterGroup group in acceptedTypes) { + for (FileTypeFilterGroup group in acceptedTypes ?? []) { allExtensions += group.fileExtensions; } String acceptedTypeString = allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; diff --git a/packages/file_picker/file_picker_web/test/file_picker_web_test.dart b/packages/file_picker/file_picker_web/test/file_picker_web_test.dart index d471ab5e3446..97902b475071 100644 --- a/packages/file_picker/file_picker_web/test/file_picker_web_test.dart +++ b/packages/file_picker/file_picker_web/test/file_picker_web_test.dart @@ -4,6 +4,7 @@ @TestOn('chrome') // Uses web-only Flutter SDK +import 'dart:typed_data'; import 'dart:html' as html; import 'package:flutter_test/flutter_test.dart'; import 'package:file_picker_web/file_picker_web.dart'; @@ -14,4 +15,12 @@ import 'package:platform_detect/test_utils.dart' as platform; class MockWindow extends Mock implements html.Window {} void main() { + // Under test.. + FilePicker plugin; + + setUp(() { + plugin = FilePicker(); + }); + + } \ No newline at end of file From 80d4dbdead51e7834149e5c98db35391040bf7c9 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 28 Jul 2020 17:27:29 -0700 Subject: [PATCH 075/151] More Refactor of loadFile and saveFile into Smaller Pieces (Prep for Testing) --- .../file_picker_web/lib/file_picker_web.dart | 63 +++++++++++++------ 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 5dea1ad162ab..73f54588b10f 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -36,32 +36,20 @@ class FilePickerPlugin extends FilePickerPlatform { void _addElementToDomAndClick(Element element) { // Add the file input element and click it + // All previous elements will be removed before adding the new one _target.children.clear(); _target.children.add(element); element.click(); } - /// Load file from user's computer and return it as an XFile - Future> loadFile({List acceptedTypes}) { - List allExtensions = List(); - for (FileTypeFilterGroup group in acceptedTypes ?? []) { - allExtensions += group.fileExtensions; - } - String acceptedTypeString = allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; - - final FileUploadInputElement element = _createFileInputElement(acceptedTypeString); - - _addElementToDomAndClick(element); - - final Completer> _completer = Completer(); - - // Get the returned files - // TODO: Handle errors + Future> _getFileFromInputElement(InputElement element) { + // Listens for element change element.onChange.first.then((event) { // File type from dart:html class List files = element.files; List returnFiles = List(); + // Create XFiles from dart:html Files for (File file in files) { String url = Url.createObjectUrl(file); String name = file.name; @@ -72,10 +60,40 @@ class FilePickerPlugin extends FilePickerPlatform { _completer.complete(returnFiles); }); - + + element.onError.first.then((event) { + if (!_completer.isCompleted) { + _completer.completeError(event); + } + }); + return _completer.future; } + /// Load file from user's computer and return it as an XFile + @override + Future> loadFile({List acceptedTypes}) { + List allExtensions = List(); + for (FileTypeFilterGroup group in acceptedTypes ?? []) { + allExtensions += group.fileExtensions; + } + String acceptedTypeString = allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; + + final FileUploadInputElement element = _createFileInputElement(acceptedTypeString); + + _addElementToDomAndClick(element); + + final Completer> _completer = Completer(); + + return _getFileFromInputElement(element); + } + + AnchorElement _createAnchorElement(String href, String suggestedName) { + final AnchorElement element = AnchorElement(href: url); + element.download = suggestedName; + return element; + } + /// Web implementation of saveFile() @override void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { @@ -87,13 +105,12 @@ class FilePickerPlugin extends FilePickerPlatform { String url = Url.createObjectUrl(blob); // Create an tag with the appropriate download attributes and click it - final AnchorElement element = AnchorElement(href: url); - element.download = suggestedName; + final AnchorElement element = _createAnchorElement(href: url, suggestedName: suggestedName); _addElementToDomAndClick(element); } - /// Initializes a DOM container where we can host input elements. + /// Initializes a DOM container where we can host elements. Element _ensureInitialized(String id) { var target = querySelector('#${id}'); if (target == null) { @@ -106,3 +123,9 @@ class FilePickerPlugin extends FilePickerPlatform { return target; } } + +/// Overrides some functions to allow testing +@visibleForTesting +class FilePickerPluginTestOverrides { + +} \ No newline at end of file From a0239381845be44432c062b9850ee2ebb033295f Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Thu, 30 Jul 2020 17:45:21 -0700 Subject: [PATCH 076/151] Initial tests for web implementation Also some refactoring of plugin code to accomplish tests --- .../file_picker_web/lib/file_picker_web.dart | 89 ++++++++++++++----- .../file_picker_web/test/README.md | 17 ++++ .../test/file_picker_web_test.dart | 46 ++++++++-- .../file_picker_web/test/lib/main.dart | 22 +++++ .../file_picker_web/test/pubspec.yaml | 23 +++++ .../test/test_driver/web_e2e.dart | 50 +++++++++++ .../test/test_driver/web_e2e_test.dart | 7 ++ .../file_picker_web/test/web/index.html | 13 +++ 8 files changed, 238 insertions(+), 29 deletions(-) create mode 100644 packages/file_picker/file_picker_web/test/README.md create mode 100644 packages/file_picker/file_picker_web/test/lib/main.dart create mode 100644 packages/file_picker/file_picker_web/test/pubspec.yaml create mode 100644 packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart create mode 100644 packages/file_picker/file_picker_web/test/test_driver/web_e2e_test.dart create mode 100644 packages/file_picker/file_picker_web/test/web/index.html diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 73f54588b10f..ada730e1932a 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:meta/meta.dart'; final String _kFilePickerInputsDomId = '__file_picker_web-file-input'; @@ -12,10 +13,15 @@ final String _kFilePickerInputsDomId = '__file_picker_web-file-input'; /// This class implements the `package:file_picker` functionality for the web. class FilePickerPlugin extends FilePickerPlatform { Element _target; - - /// Default constructor, initializes _target to a DOM element - /// that we can use to host HTML elements - FilePickerPlugin() { + final FilePickerPluginTestOverrides _overrides; + bool get _hasTestOverrides => _overrides != null; + + /// Default constructor, initializes _target to a DOM element that we can use + /// to host HTML elements. + /// overrides parameter allows for testing to override functions + FilePickerPlugin({ + @visibleForTesting FilePickerPluginTestOverrides overrides, + }) : _overrides = overrides { _target = _ensureInitialized(_kFilePickerInputsDomId); } @@ -23,8 +29,23 @@ class FilePickerPlugin extends FilePickerPlatform { static void registerWith(Registrar registrar) { FilePickerPlatform.instance = FilePickerPlugin(); } + + /// Convert list of filter groups to a comma-separated string + String _getStringFromFilterGroup (List acceptedTypes) { + List allExtensions = List(); + for (FileTypeFilterGroup group in acceptedTypes ?? []) { + allExtensions += group.fileExtensions; + } + return allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; + } - FileUploadInputElement _createFileInputElement(String accepted) { + /// Creates a file input element with only the accept attribute + @visibleForTesting + FileUploadInputElement createFileInputElement(String accepted) { + if (_hasTestOverrides) { + return _overrides.createFileInputElement(accepted); + } + final FileUploadInputElement element = FileUploadInputElement(); if (accepted.isNotEmpty) { element.accept = accepted; @@ -42,11 +63,39 @@ class FilePickerPlugin extends FilePickerPlatform { element.click(); } - Future> _getFileFromInputElement(InputElement element) { + List _getXFilesFromFiles (List files) { + List xFiles = List(); + + for (File file in files) { + String url = Url.createObjectUrl(file); + String name = file.name; + int length = file.size; + + xFiles.add(XFile(url, name: name, length: length)); + } + + return xFiles; + } + + /// Getter for retrieving files from an input element + @visibleForTesting + List getFilesFromInputElement(InputElement element) { + if(_hasTestOverrides) { + return _overrides.getFilesFromInputElement(element); + } + + return element?.files ?? []; + } + + /// Listen for file input element to change and retrieve files when + /// this happens. + Future> _getFilesWhenReady(InputElement element) { + final Completer> _completer = Completer(); + // Listens for element change element.onChange.first.then((event) { // File type from dart:html class - List files = element.files; + List files = getFilesFromInputElement(element); List returnFiles = List(); // Create XFiles from dart:html Files @@ -73,23 +122,17 @@ class FilePickerPlugin extends FilePickerPlatform { /// Load file from user's computer and return it as an XFile @override Future> loadFile({List acceptedTypes}) { - List allExtensions = List(); - for (FileTypeFilterGroup group in acceptedTypes ?? []) { - allExtensions += group.fileExtensions; - } - String acceptedTypeString = allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; + final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); - final FileUploadInputElement element = _createFileInputElement(acceptedTypeString); + final FileUploadInputElement element = createFileInputElement(acceptedTypeString); _addElementToDomAndClick(element); - - final Completer> _completer = Completer(); - - return _getFileFromInputElement(element); + + return _getFilesWhenReady(element); } AnchorElement _createAnchorElement(String href, String suggestedName) { - final AnchorElement element = AnchorElement(href: url); + final AnchorElement element = AnchorElement(href: href); element.download = suggestedName; return element; } @@ -98,14 +141,12 @@ class FilePickerPlugin extends FilePickerPlatform { @override void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { // Create blob from data - // TODO: Handle different types - final Blob blob = type.isEmpty ? Blob([data]) : Blob([data], type); String url = Url.createObjectUrl(blob); // Create an tag with the appropriate download attributes and click it - final AnchorElement element = _createAnchorElement(href: url, suggestedName: suggestedName); + final AnchorElement element = _createAnchorElement(url, suggestedName); _addElementToDomAndClick(element); } @@ -127,5 +168,9 @@ class FilePickerPlugin extends FilePickerPlatform { /// Overrides some functions to allow testing @visibleForTesting class FilePickerPluginTestOverrides { - + /// For overriding the creation of the file input element. + Element Function(String accepted) createFileInputElement; + + /// For overriding retrieving a file from the input element. + List Function(InputElement input) getFilesFromInputElement; } \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/test/README.md b/packages/file_picker/file_picker_web/test/README.md new file mode 100644 index 000000000000..f623e9fd08d3 --- /dev/null +++ b/packages/file_picker/file_picker_web/test/README.md @@ -0,0 +1,17 @@ +# Running browser_tests + +Make sure you have updated to the latest Flutter master. + +1. Check what version of Chrome is running on the machine you're running tests on. + +2. Download and install driver for that version from here: + * + +3. Start the driver using `chromedriver --port=4444` + +4. Change into the `test` directory of your clone. + +5. Run tests: `flutter drive -d web-server --release --browser-name=chrome --target=test_driver/TEST_NAME_e2e.dart`, or (in Linux): + + * Single: `./run_test test_driver/TEST_NAME_e2e.dart` + * All: `./run_test` diff --git a/packages/file_picker/file_picker_web/test/file_picker_web_test.dart b/packages/file_picker/file_picker_web/test/file_picker_web_test.dart index 97902b475071..830bb19b9be3 100644 --- a/packages/file_picker/file_picker_web/test/file_picker_web_test.dart +++ b/packages/file_picker/file_picker_web/test/file_picker_web_test.dart @@ -2,25 +2,57 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@TestOn('chrome') // Uses web-only Flutter SDK +//@TestOn('chrome') // Uses web-only Flutter SDK +import 'dart:convert'; import 'dart:typed_data'; -import 'dart:html' as html; +import 'dart:html'; + import 'package:flutter_test/flutter_test.dart'; import 'package:file_picker_web/file_picker_web.dart'; -import 'package:mockito/mockito.dart'; +import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; import 'package:platform_detect/test_utils.dart' as platform; -class MockWindow extends Mock implements html.Window {} +final String expectedStringContents = 'Hello, world!'; +final expectedSize = expectedStringContents.length; +final Uint8List bytes = utf8.encode(expectedStringContents); +final File textFile = File([bytes], 'hello.txt'); void main() { // Under test.. - FilePicker plugin; + FilePickerPlugin plugin; setUp(() { - plugin = FilePicker(); + plugin = FilePickerPlugin(); + }); + + test('Basic', () { expect(1+1, 2); }); +/* + test('Select a file to load', () async { + final mockInput = FileUploadInputElement(); + + final plugin = ImagePickerPlugin( + overrides: FilePickerPluginTestOverrides() + ..createFileInputElement((_) => mockInput) + ..getFileFromInputElement((_) => textFile) + ); + + // Call load file + final XFile file = plugin.loadFile(); + + // Mock selection of a file + mockInput.dispatchEvent(Event('change')); + + // Expect the file to complete + expect(file, completes); + + // Expect that we can read from the file + final loadedFile = await file; + expect(loadedFile.readAsBytes(), completion(isNotEmpty)); + expect(pickedFile.length(), completion(equals(expectedSize))); + expect(loadedFile.name, 'hello.txt'); }); - + */ } \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/test/lib/main.dart b/packages/file_picker/file_picker_web/test/lib/main.dart new file mode 100644 index 000000000000..4c55f6c95a7a --- /dev/null +++ b/packages/file_picker/file_picker_web/test/lib/main.dart @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatefulWidget { + @override + MyAppState createState() => MyAppState(); +} + +class MyAppState extends State { + @override + Widget build(BuildContext context) { + return Text('Testing... Look at the console output for results!'); + } +} + diff --git a/packages/file_picker/file_picker_web/test/pubspec.yaml b/packages/file_picker/file_picker_web/test/pubspec.yaml new file mode 100644 index 000000000000..c59457c1d9d6 --- /dev/null +++ b/packages/file_picker/file_picker_web/test/pubspec.yaml @@ -0,0 +1,23 @@ +name: regular_integration_tests +publish_to: none + +environment: + sdk: ">=2.2.2 <3.0.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + file_picker_web: + path: ../ + file_picker_platform_interface: + path: ../../file_picker_platform_interface + google_maps: ^3.4.4 + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + e2e: ^0.6.1 + http: ^0.12.2 + mockito: ^4.1.1 \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart b/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart new file mode 100644 index 000000000000..44290a37cd2f --- /dev/null +++ b/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart @@ -0,0 +1,50 @@ +import 'dart:async'; + +import 'package:e2e/e2e.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + + +import 'dart:convert'; +import 'dart:typed_data'; +import 'dart:html'; + +import 'package:file_picker_web/file_picker_web.dart'; +import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; + +import 'package:platform_detect/test_utils.dart' as platform; + +final String expectedStringContents = 'Hello, world!'; +final expectedSize = expectedStringContents.length; +final Uint8List bytes = utf8.encode(expectedStringContents); +final File textFile = File([bytes], 'hello.txt'); + +/// Test Markers +void main() { + E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; + + test('Select a single file to load', () async { + final mockInput = FileUploadInputElement(); + + final plugin = FilePickerPlugin( + overrides: FilePickerPluginTestOverrides() + ..createFileInputElement = ((_) => mockInput) + ..getFilesFromInputElement = ((_) => [textFile]) + ); + + // Call load file + final files = plugin.loadFile(); + // Mock selection of a file + mockInput.dispatchEvent(Event('change')); + + // Expect the file to complete + expect(files, completes); + + // Expect that we can read from the file + final loadedFiles = await files; + final loadedFile = loadedFiles.first; + expect(loadedFile.readAsBytes(), completion(isNotEmpty)); + expect(loadedFile.length(), completion(equals(expectedSize))); + expect(loadedFile.name, 'hello.txt'); + }); +} diff --git a/packages/file_picker/file_picker_web/test/test_driver/web_e2e_test.dart b/packages/file_picker/file_picker_web/test/test_driver/web_e2e_test.dart new file mode 100644 index 000000000000..a29203f7dcdd --- /dev/null +++ b/packages/file_picker/file_picker_web/test/test_driver/web_e2e_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:e2e/e2e_driver.dart' as e2e; + +Future main() async => e2e.main(); diff --git a/packages/file_picker/file_picker_web/test/web/index.html b/packages/file_picker/file_picker_web/test/web/index.html new file mode 100644 index 000000000000..59a832b5de4c --- /dev/null +++ b/packages/file_picker/file_picker_web/test/web/index.html @@ -0,0 +1,13 @@ + + + + + Browser Tests + + + + + + From b0f31c38eaacd5bd2ce14b4c830506de66a18b26 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 31 Jul 2020 15:25:04 -0700 Subject: [PATCH 077/151] Delete original test file --- .../test/file_picker_web_test.dart | 58 ------------------- 1 file changed, 58 deletions(-) delete mode 100644 packages/file_picker/file_picker_web/test/file_picker_web_test.dart diff --git a/packages/file_picker/file_picker_web/test/file_picker_web_test.dart b/packages/file_picker/file_picker_web/test/file_picker_web_test.dart deleted file mode 100644 index 830bb19b9be3..000000000000 --- a/packages/file_picker/file_picker_web/test/file_picker_web_test.dart +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//@TestOn('chrome') // Uses web-only Flutter SDK - -import 'dart:convert'; -import 'dart:typed_data'; -import 'dart:html'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:file_picker_web/file_picker_web.dart'; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; - -import 'package:platform_detect/test_utils.dart' as platform; - -final String expectedStringContents = 'Hello, world!'; -final expectedSize = expectedStringContents.length; -final Uint8List bytes = utf8.encode(expectedStringContents); -final File textFile = File([bytes], 'hello.txt'); - -void main() { - // Under test.. - FilePickerPlugin plugin; - - setUp(() { - plugin = FilePickerPlugin(); - }); - - test('Basic', () { expect(1+1, 2); }); -/* - test('Select a file to load', () async { - final mockInput = FileUploadInputElement(); - - final plugin = ImagePickerPlugin( - overrides: FilePickerPluginTestOverrides() - ..createFileInputElement((_) => mockInput) - ..getFileFromInputElement((_) => textFile) - ); - - // Call load file - final XFile file = plugin.loadFile(); - - // Mock selection of a file - mockInput.dispatchEvent(Event('change')); - - // Expect the file to complete - expect(file, completes); - - // Expect that we can read from the file - final loadedFile = await file; - expect(loadedFile.readAsBytes(), completion(isNotEmpty)); - expect(pickedFile.length(), completion(equals(expectedSize))); - expect(loadedFile.name, 'hello.txt'); - }); - - */ -} \ No newline at end of file From fe2265cbd9b2502cadac2742e78d443198e48a85 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 31 Jul 2020 17:27:33 -0700 Subject: [PATCH 078/151] Test Loading Multiple Files + saveFile tests --- .../file_picker_web/lib/file_picker_web.dart | 17 ++- .../test/test_driver/web_e2e.dart | 116 ++++++++++++++---- 2 files changed, 105 insertions(+), 28 deletions(-) diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index ada730e1932a..f03663c16896 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -131,22 +131,29 @@ class FilePickerPlugin extends FilePickerPlatform { return _getFilesWhenReady(element); } - AnchorElement _createAnchorElement(String href, String suggestedName) { + /// Create anchor element with download attribute + @visibleForTesting + AnchorElement createAnchorElement(String href, String suggestedName) { final AnchorElement element = AnchorElement(href: href); element.download = suggestedName; return element; } + /// Create blob with specified data of indicated type + @visibleForTesting + Blob createBlob(Uint8List data, String type) { + return type.isEmpty ? Blob([data]) : Blob([data], type); + } + /// Web implementation of saveFile() @override void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { // Create blob from data - final Blob blob = type.isEmpty ? Blob([data]) : Blob([data], type); - + final blob = createBlob(data, type); String url = Url.createObjectUrl(blob); // Create an tag with the appropriate download attributes and click it - final AnchorElement element = _createAnchorElement(url, suggestedName); + final AnchorElement element = createAnchorElement(url, suggestedName); _addElementToDomAndClick(element); } @@ -170,7 +177,7 @@ class FilePickerPlugin extends FilePickerPlatform { class FilePickerPluginTestOverrides { /// For overriding the creation of the file input element. Element Function(String accepted) createFileInputElement; - + /// For overriding retrieving a file from the input element. List Function(InputElement input) getFilesFromInputElement; } \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart b/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart index 44290a37cd2f..6442507f0f6e 100644 --- a/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart +++ b/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart @@ -19,32 +19,102 @@ final expectedSize = expectedStringContents.length; final Uint8List bytes = utf8.encode(expectedStringContents); final File textFile = File([bytes], 'hello.txt'); +final String expectedStringContents2 = 'This is the other test file'; +final expectedSize2 = expectedStringContents.length; +final Uint8List bytes2 = utf8.encode(expectedStringContents); +final File textFile2 = File([bytes], 'test2.txt'); + + /// Test Markers void main() { E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; - test('Select a single file to load', () async { - final mockInput = FileUploadInputElement(); - - final plugin = FilePickerPlugin( - overrides: FilePickerPluginTestOverrides() - ..createFileInputElement = ((_) => mockInput) - ..getFilesFromInputElement = ((_) => [textFile]) - ); - - // Call load file - final files = plugin.loadFile(); - // Mock selection of a file - mockInput.dispatchEvent(Event('change')); - - // Expect the file to complete - expect(files, completes); - - // Expect that we can read from the file - final loadedFiles = await files; - final loadedFile = loadedFiles.first; - expect(loadedFile.readAsBytes(), completion(isNotEmpty)); - expect(loadedFile.length(), completion(equals(expectedSize))); - expect(loadedFile.name, 'hello.txt'); + group('loadFile: ', () { + test('Select a single file to load', () async { + final mockInput = FileUploadInputElement(); + + // Note that we override the retrieval of files from the input element. + // We opt to do this because dart cannot edit the "files" attribute of + // tag. When performing mockInput.files = [ textFile ] we receive: + // "Failed to set the 'files' property on 'HTMLInputElement': The provided + // value is not of type 'FileList'." + // + // More on this (javascript side): https://stackoverflow.com/questions/52078853/is-it-possible-to-update-filelist + // The dart implementation will depend on the javascript it creates. + final plugin = FilePickerPlugin( + overrides: FilePickerPluginTestOverrides() + ..createFileInputElement = ((_) => mockInput) + ..getFilesFromInputElement = ((_) => [textFile]) + ); + + // Call load file + final files = plugin.loadFile(); + + // Mock selection of a file + mockInput.dispatchEvent(Event('change')); + + // Expect the file to complete + expect(files, completes); + + // Expect that we can read from the file + final loadedFiles = await files; + final loadedFile = loadedFiles.first; + expect(loadedFile.readAsBytes(), completion(isNotEmpty)); + expect(loadedFile.length(), completion(equals(expectedSize))); + expect(loadedFile.name, textFile.name); + }); + + test('Select multiple files to load', () async { + final mockInput = FileUploadInputElement(); + + final plugin = FilePickerPlugin( + overrides: FilePickerPluginTestOverrides() + ..createFileInputElement = ((_) => mockInput) + ..getFilesFromInputElement = ((_) => [textFile, textFile2]) + ); + + // Call load file + final files = plugin.loadFile(); + + // Mock selection of a file + mockInput.dispatchEvent(Event('change')); + + // Expect the file to complete + expect(files, completes); + + // Expect that we can read from the files + final loadedFiles = await files; + final file1 = loadedFiles[0]; + expect(file1.readAsBytes(), completion(isNotEmpty)); + expect(file1.length(), completion(equals(expectedSize))); + expect(file1.name, textFile.name); + + final file2 = loadedFiles[1]; + expect(file2.readAsBytes(), completion(isNotEmpty)); + expect(file2.length(), completion(equals(expectedSize2))); + expect(file2.name, textFile2.name); + }); + }); + + group('saveFile: ', () { + test('Create a blob', () { + Uint8List data = Uint8List.fromList(expectedStringContents.codeUnits); + + FilePickerPlugin plugin = FilePickerPlugin(); + final blob = plugin.createBlob(data, 'text/plain'); + + expect(blob.type, 'text/plain'); + expect(blob.size, expectedSize); + }); + + test('Create an anchor', () { + FilePickerPlugin plugin = FilePickerPlugin(); + final String href = 'https://google.com'; + final String name = 'file_name.txt'; + final anchor = plugin.createAnchorElement(href, name); + + expect(anchor.download, name); + expect(anchor.href, href); + }); }); } From 53bea2810449819e686dd01b0a71a17e0a4c41dd Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 4 Aug 2020 11:27:32 -0700 Subject: [PATCH 079/151] Fix Example Text Overflow --- packages/file_picker/file_picker/example/lib/main.dart | 5 ++++- packages/file_picker/file_picker/example/pubspec.yaml | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index 5f4ab99e2ef6..500a4b7c073c 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -92,6 +92,8 @@ class _MyHomePageState extends State { Container( width: 300, child: TextField( + minLines: 1, + maxLines: 12, controller: _nameController, decoration: InputDecoration( hintText: '(Optional) Suggest File Name', @@ -101,7 +103,8 @@ class _MyHomePageState extends State { Container( width: 300, child: TextField( - maxLines: null, + minLines: 1, + maxLines: 12, controller: _fileController, decoration: InputDecoration( hintText: 'Enter File Contents', diff --git a/packages/file_picker/file_picker/example/pubspec.yaml b/packages/file_picker/file_picker/example/pubspec.yaml index baa9292c63cc..c292d1b300aa 100644 --- a/packages/file_picker/file_picker/example/pubspec.yaml +++ b/packages/file_picker/file_picker/example/pubspec.yaml @@ -27,7 +27,6 @@ dependencies: file_picker: path: ../ - # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.3 From f1383ea12342bd6b28b8e1de90de76541f1c1d74 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 4 Aug 2020 15:22:40 -0700 Subject: [PATCH 080/151] Add XPath class and New API to interface --- .../file_picker_interface.dart | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index cfe5a437ea29..76a9a5164ec6 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -10,6 +10,20 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import '../method_channel/method_channel_file_picker.dart'; + +// TODO: move to its own file +/// Cross platform path class +class XPath { + /// XPath constructor + XPath(this._path, {int modified, int created}): + _modified = modified, + _created = created; + + final String _path; + final int _modified; + final int _created; +} + /// The interface that implementations of file_picker must implement. /// /// Platform implementations should extend this class rather than implement it as `file_picker` @@ -37,6 +51,24 @@ abstract class FilePickerPlatform extends PlatformInterface { _instance = instance; } + /// Open file dialog for loading files and return a file path + XPath getReadPath() { + throw UnimplementedError('getReadPath() has not been implemented.'); + } + + /// Open file dialog for loading files and return a list of file paths + List getReadPaths() { + throw UnimplementedError('getReadPaths() has not been implemented.'); + } + + /// Open file dialog for saving files and return a file path at which to save + XPath getSavePath() { + throw UnimplementedError('loadFile() has not been implemented.'); + } + + + /// OLD API: + /// Load file from user's computer and return it as an XFile Future> loadFile({List acceptedTypes}) { throw UnimplementedError('loadFile() has not been implemented.'); From d879cc3f49bc04b063b437b76ea101c93e1931a7 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 4 Aug 2020 17:02:14 -0700 Subject: [PATCH 081/151] Add XPath and begin getReadPath(s) --- .../file_picker/lib/file_picker.dart | 20 +++ .../file_picker_interface.dart | 19 +-- .../lib/src/types/types.dart | 1 + .../lib/src/types/x_path/x_path.dart | 18 +++ .../file_picker_web/lib/file_picker_web.dart | 115 +++++++++++------- 5 files changed, 114 insertions(+), 59 deletions(-) create mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/x_path/x_path.dart diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index 5e10892fe44b..0ae8206f57db 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -10,6 +10,26 @@ import 'package:file_picker_platform_interface/file_picker_platform_interface.da export 'package:file_picker_platform_interface/file_picker_platform_interface.dart' show XFile, FileTypeFilterGroup; +/// NEW API + +/// Open file dialog for loading files and return a file path +XPath getReadPath({List acceptedTypes}) { + return FilePickerPlatform.instance.getReadPath(acceptedTypes: acceptedTypes); +} + +/// Open file dialog for loading files and return a list of file paths +List getReadPaths({List acceptedTypes}) { + return FilePickerPlatform.instance.getReadPaths(acceptedTypes: acceptedTypes); +} + +/// Open file dialog for saving files and return a file path at which to save +XPath getSavePath() { + throw UnimplementedError('loadFile() has not been implemented.'); +} + + +/// OLD API + /// Saves File to user's file system void saveFile(Uint8List data, {String type = '', String suggestedName}) async { return FilePickerPlatform.instance.saveFile(data, type: type, suggestedName: suggestedName); diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index 76a9a5164ec6..2d9fdc1178fd 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -11,18 +11,7 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import '../method_channel/method_channel_file_picker.dart'; -// TODO: move to its own file -/// Cross platform path class -class XPath { - /// XPath constructor - XPath(this._path, {int modified, int created}): - _modified = modified, - _created = created; - - final String _path; - final int _modified; - final int _created; -} + /// The interface that implementations of file_picker must implement. /// @@ -52,17 +41,17 @@ abstract class FilePickerPlatform extends PlatformInterface { } /// Open file dialog for loading files and return a file path - XPath getReadPath() { + Future getReadPath({List acceptedTypes}) { throw UnimplementedError('getReadPath() has not been implemented.'); } /// Open file dialog for loading files and return a list of file paths - List getReadPaths() { + Future> getReadPaths({List acceptedTypes}) { throw UnimplementedError('getReadPaths() has not been implemented.'); } /// Open file dialog for saving files and return a file path at which to save - XPath getSavePath() { + Future getSavePath() { throw UnimplementedError('loadFile() has not been implemented.'); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart index 24e7e7ff83dd..1c7bd02345c5 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart @@ -3,6 +3,7 @@ export 'image_source.dart'; export 'lost_data_response.dart'; export 'retrieve_type.dart'; export 'x_file/x_file.dart'; +export 'x_path/x_path.dart'; export 'filter_group/filter_group.dart'; diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_path/x_path.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_path/x_path.dart new file mode 100644 index 000000000000..07df81e9dce8 --- /dev/null +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_path/x_path.dart @@ -0,0 +1,18 @@ +/// Cross platform path class +class XPath { + /// XPath constructor + XPath(this._path, {String name, int modified, int created}): + _modified = modified, + _created = created, + _name = name; + + final String _path; + final String _name; + final int _modified; + final int _created; + + String get path => _path; + int get modified => _modified; + int get created => _created; + String get name => _name; +} \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index f03663c16896..24048653bb13 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -41,7 +41,7 @@ class FilePickerPlugin extends FilePickerPlatform { /// Creates a file input element with only the accept attribute @visibleForTesting - FileUploadInputElement createFileInputElement(String accepted) { + FileUploadInputElement createFileInputElement(String accepted, bool multiple) { if (_hasTestOverrides) { return _overrides.createFileInputElement(accepted); } @@ -50,7 +50,7 @@ class FilePickerPlugin extends FilePickerPlatform { if (accepted.isNotEmpty) { element.accept = accepted; } - element.multiple = true; + element.multiple = multiple; return element; } @@ -63,18 +63,19 @@ class FilePickerPlugin extends FilePickerPlatform { element.click(); } - List _getXFilesFromFiles (List files) { - List xFiles = List(); + List _getXPathsFromFiles (List files) { + List xPaths = List(); for (File file in files) { String url = Url.createObjectUrl(file); String name = file.name; int length = file.size; + int modified = file.lastModified; - xFiles.add(XFile(url, name: name, length: length)); + xPaths.add(XPath(url, name: name, modified: modified)); } - return xFiles; + return xPaths; } /// Getter for retrieving files from an input element @@ -87,27 +88,34 @@ class FilePickerPlugin extends FilePickerPlatform { return element?.files ?? []; } + Future _getFileWhenReady(InputElement element) { + final Completer _completer = Completer(); + + _getFilesWhenReady(element) + .then((list) { + _completer.complete(list[0]); + }) + .catchError((err) { + _completer.completeError(err); + }); + + return _completer.future; + } + /// Listen for file input element to change and retrieve files when /// this happens. - Future> _getFilesWhenReady(InputElement element) { + Future> _getFilesWhenReady(InputElement element) { final Completer> _completer = Completer(); // Listens for element change element.onChange.first.then((event) { // File type from dart:html class - List files = getFilesFromInputElement(element); - List returnFiles = List(); + final List files = getFilesFromInputElement(element); - // Create XFiles from dart:html Files - for (File file in files) { - String url = Url.createObjectUrl(file); - String name = file.name; - int length = file.size; - - returnFiles.add(XFile(url, name: name, length: length)); - } + // Create XPath from dart:html Files + final returnPaths = _getXPathsFromFiles(files); - _completer.complete(returnFiles); + _completer.complete(returnPaths); }); element.onError.first.then((event) { @@ -119,22 +127,10 @@ class FilePickerPlugin extends FilePickerPlatform { return _completer.future; } - /// Load file from user's computer and return it as an XFile - @override - Future> loadFile({List acceptedTypes}) { - final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); - - final FileUploadInputElement element = createFileInputElement(acceptedTypeString); - - _addElementToDomAndClick(element); - - return _getFilesWhenReady(element); - } - /// Create anchor element with download attribute @visibleForTesting AnchorElement createAnchorElement(String href, String suggestedName) { - final AnchorElement element = AnchorElement(href: href); + final element = AnchorElement(href: href); element.download = suggestedName; return element; } @@ -145,19 +141,6 @@ class FilePickerPlugin extends FilePickerPlatform { return type.isEmpty ? Blob([data]) : Blob([data], type); } - /// Web implementation of saveFile() - @override - void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { - // Create blob from data - final blob = createBlob(data, type); - String url = Url.createObjectUrl(blob); - - // Create an tag with the appropriate download attributes and click it - final AnchorElement element = createAnchorElement(url, suggestedName); - - _addElementToDomAndClick(element); - } - /// Initializes a DOM container where we can host elements. Element _ensureInitialized(String id) { var target = querySelector('#${id}'); @@ -170,6 +153,50 @@ class FilePickerPlugin extends FilePickerPlatform { } return target; } + + /// NEW API + + // Load Helper + Future> _readPathHelper (bool multiple, List acceptedTypes) { + final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); + + final FileUploadInputElement element = createFileInputElement(acceptedTypeString, multiple); + + _addElementToDomAndClick(element); + + return _getFilesWhenReady(element); + } + + /// Open file dialog for loading files and return a file path + @override + Future getReadPath({List acceptedTypes}) { + return _readPathHelper(false, acceptedTypes); + } + + /// Open file dialog for loading files and return a list of file paths + @override + Future> getReadPaths({List acceptedTypes}) { + return _readPathHelper(true, acceptedTypes); + } + + /// Open file dialog for saving files and return a file path at which to save + @override + Future getSavePath() { + throw UnimplementedError('loadFile() has not been implemented.'); + } + + /// Web implementation of saveFile() + @override + void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { + // Create blob from data + final blob = createBlob(data, type); + String url = Url.createObjectUrl(blob); + + // Create an tag with the appropriate download attributes and click it + final AnchorElement element = createAnchorElement(url, suggestedName); + + _addElementToDomAndClick(element); + } } /// Overrides some functions to allow testing From 8d13d4dc7d8e110d5d34711536d62ba99dae0f42 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 4 Aug 2020 17:22:24 -0700 Subject: [PATCH 082/151] Functional getReadPath --- .../file_picker/example/lib/main.dart | 18 ++++++++++-------- .../file_picker/lib/file_picker.dart | 8 ++++---- .../lib/src/types/x_file/html.dart | 18 +++++++++++++++++- .../file_picker_web/lib/file_picker_web.dart | 12 ++++++++++-- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index 500a4b7c073c..10ff2e85f0ce 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -60,21 +60,23 @@ class _MyHomePageState extends State { } void _loadFile() async { - List file; + XPath path; if (_extensionController.text.isNotEmpty) { - List type = List(); - type.add(FileTypeFilterGroup(label: 'Example Files', fileExtensions: _extensionController.text.split(','))); - file = await loadFile(acceptedTypes: type); + List types = List(); + types.add(FileTypeFilterGroup(label: 'Example Files', fileExtensions: _extensionController.text.split(','))); + path = await getReadPath(acceptedTypes: types); } else { - file = await loadFile(); + path = await getReadPath(); } - String text = await file.first.readAsString(); + XFile file = XFile.fromXPath(path); + + String text = await file.readAsString(); _fileController.text = text; - if (file.first.name.isNotEmpty) { - _nameController.text = file.first.name; + if (file.name.isNotEmpty) { + _nameController.text = file.name; } } diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index 0ae8206f57db..c7644ae05a11 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -8,22 +8,22 @@ import 'dart:typed_data'; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; export 'package:file_picker_platform_interface/file_picker_platform_interface.dart' - show XFile, FileTypeFilterGroup; + show XFile, FileTypeFilterGroup, XPath; /// NEW API /// Open file dialog for loading files and return a file path -XPath getReadPath({List acceptedTypes}) { +Future getReadPath({List acceptedTypes}) { return FilePickerPlatform.instance.getReadPath(acceptedTypes: acceptedTypes); } /// Open file dialog for loading files and return a list of file paths -List getReadPaths({List acceptedTypes}) { +Future> getReadPaths({List acceptedTypes}) { return FilePickerPlatform.instance.getReadPaths(acceptedTypes: acceptedTypes); } /// Open file dialog for saving files and return a file path at which to save -XPath getSavePath() { +Future getSavePath() { throw UnimplementedError('loadFile() has not been implemented.'); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart index e5ecd11fee38..bfac1d3ffaff 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart @@ -5,11 +5,14 @@ import 'package:http/http.dart' as http show readBytes; import './base.dart'; +import '../types.dart'; + /// A XFile that works on web. /// /// It wraps the bytes of a selected file. class XFile extends XFileBase { - final String path; + final String path; // TODO: get rid of this guy + final XPath xPath; final Uint8List _initBytes; final int _length; @override @@ -22,15 +25,28 @@ class XFile extends XFileBase { /// /// `name` needs to be passed from the outside, since we only have /// access to it while we create the ObjectUrl. + // TODO: Replace this constructor XFile( this.path, { this.name, int length, Uint8List bytes, + this.xPath , }) : _initBytes = bytes, _length = length, super(path); + /// Constructor from XPath + XFile.fromXPath( + this.xPath, { + int length, + Uint8List bytes, + }) : _initBytes = bytes, + _length = length, + path = xPath.path, // TODO: just replace path everywhere + name = xPath.name, + super(xPath.path); + Future get _bytes async { if (_initBytes != null) { return Future.value(UnmodifiableUint8ListView(_initBytes)); diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 24048653bb13..7c9f949c16b2 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -105,7 +105,7 @@ class FilePickerPlugin extends FilePickerPlatform { /// Listen for file input element to change and retrieve files when /// this happens. Future> _getFilesWhenReady(InputElement element) { - final Completer> _completer = Completer(); + final Completer> _completer = Completer(); // Listens for element change element.onChange.first.then((event) { @@ -170,7 +170,15 @@ class FilePickerPlugin extends FilePickerPlatform { /// Open file dialog for loading files and return a file path @override Future getReadPath({List acceptedTypes}) { - return _readPathHelper(false, acceptedTypes); + Completer _completer = Completer(); + _readPathHelper(false, acceptedTypes).then((list) { + _completer.complete(list.first); + }) + .catchError((err) { + _completer.completeError(err); + }); + + return _completer.future; } /// Open file dialog for loading files and return a list of file paths From f600526a96b7a9d76e71db8dee76a4ec32db6df0 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 4 Aug 2020 17:26:09 -0700 Subject: [PATCH 083/151] Do Nothing in getSavePath web --- packages/file_picker/file_picker_web/lib/file_picker_web.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 7c9f949c16b2..74e8e5595518 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -190,7 +190,7 @@ class FilePickerPlugin extends FilePickerPlatform { /// Open file dialog for saving files and return a file path at which to save @override Future getSavePath() { - throw UnimplementedError('loadFile() has not been implemented.'); + return Future.value(); } /// Web implementation of saveFile() From c65cede07d729e264314d7c469663e46f981f532 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 4 Aug 2020 17:39:38 -0700 Subject: [PATCH 084/151] Refactor XFile Class to use XPath --- .../lib/src/types/camera_device.dart | 18 ------- .../lib/src/types/image_source.dart | 12 ----- .../lib/src/types/lost_data_response.dart | 52 ------------------- .../lib/src/types/retrieve_type.dart | 12 ----- .../lib/src/types/types.dart | 4 -- .../lib/src/types/x_file/base.dart | 4 +- .../lib/src/types/x_file/html.dart | 13 +++-- .../lib/src/types/x_file/io.dart | 6 ++- .../lib/src/types/x_file/lost_data.dart | 49 ----------------- .../lib/src/types/x_file/unsupported.dart | 3 +- .../lib/src/types/x_file/x_file.dart | 1 - 11 files changed, 15 insertions(+), 159 deletions(-) delete mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/camera_device.dart delete mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/image_source.dart delete mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/lost_data_response.dart delete mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/retrieve_type.dart delete mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/lost_data.dart diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/camera_device.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/camera_device.dart deleted file mode 100644 index 6c70fd451a0e..000000000000 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/camera_device.dart +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/// Which camera to use when picking images/videos while source is `ImageSource.camera`. -/// -/// Not every device supports both of the positions. -enum CameraDevice { - /// Use the rear camera. - /// - /// In most of the cases, it is the default configuration. - rear, - - /// Use the front camera. - /// - /// Supported on all iPhones/iPads and some Android devices. - front, -} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/image_source.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/image_source.dart deleted file mode 100644 index 37981e3038f1..000000000000 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/image_source.dart +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/// Specifies the source where the picked image should come from. -enum ImageSource { - /// Opens up the device camera, letting the user to take a new picture. - camera, - - /// Opens the user's photo gallery. - gallery, -} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/lost_data_response.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/lost_data_response.dart deleted file mode 100644 index 73014654f6e6..000000000000 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/lost_data_response.dart +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:io'; - -import 'package:flutter/services.dart'; -import 'types.dart'; // TODO: use 'package:...' instead - -/// The response object of [ImagePicker.retrieveLostData]. -/// -/// Only applies to Android. -/// See also: -/// * [ImagePicker.retrieveLostData] for more details on retrieving lost data. -@Deprecated('Use methods that return a LostData object instead.') -class LostDataResponse { - /// Creates an instance with the given [file], [exception], and [type]. Any of - /// the params may be null, but this is never considered to be empty. - LostDataResponse({this.file, this.exception, this.type}); - - /// Initializes an instance with all member params set to null and considered - /// to be empty. - LostDataResponse.empty() - : file = null, - exception = null, - type = null, - _empty = true; - - /// Whether it is an empty response. - /// - /// An empty response should have [file], [exception] and [type] to be null. - bool get isEmpty => _empty; - - /// The file that was lost in a previous [pickImage] or [pickVideo] call due to MainActivity being destroyed. - /// - /// Can be null if [exception] exists. - final File file; - - /// The exception of the last [pickImage] or [pickVideo]. - /// - /// If the last [pickImage] or [pickVideo] threw some exception before the MainActivity destruction, this variable keeps that - /// exception. - /// You should handle this exception as if the [pickImage] or [pickVideo] got an exception when the MainActivity was not destroyed. - /// - /// Note that it is not the exception that caused the destruction of the MainActivity. - final PlatformException exception; - - /// Can either be [RetrieveType.image] or [RetrieveType.video]; - final RetrieveType type; - - bool _empty = false; -} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/retrieve_type.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/retrieve_type.dart deleted file mode 100644 index cc32be9711c2..000000000000 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/retrieve_type.dart +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/// The type of the retrieved data in a [LostDataResponse]. -enum RetrieveType { - /// A static picture. See [ImagePicker.pickImage]. - image, - - /// A video. See [ImagePicker.pickVideo]. - video -} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart index 1c7bd02345c5..b8adfaa2932e 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart @@ -1,7 +1,3 @@ -export 'camera_device.dart'; -export 'image_source.dart'; -export 'lost_data_response.dart'; -export 'retrieve_type.dart'; export 'x_file/x_file.dart'; export 'x_path/x_path.dart'; diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart index 4d12145ce14b..140d8f6edb12 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart @@ -1,6 +1,8 @@ import 'dart:convert'; import 'dart:typed_data'; +import '../types.dart'; + /// The interface for a XFile. /// /// A XFile is a container that wraps the path of a selected @@ -11,7 +13,7 @@ import 'dart:typed_data'; /// the methods should seem familiar. abstract class XFileBase { /// Construct a XFile - XFileBase(String path); + XFileBase(XPath path); /// Get the path of the picked file. /// diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart index bfac1d3ffaff..78014ff3dfcc 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart @@ -11,7 +11,6 @@ import '../types.dart'; /// /// It wraps the bytes of a selected file. class XFile extends XFileBase { - final String path; // TODO: get rid of this guy final XPath xPath; final Uint8List _initBytes; final int _length; @@ -25,16 +24,14 @@ class XFile extends XFileBase { /// /// `name` needs to be passed from the outside, since we only have /// access to it while we create the ObjectUrl. - // TODO: Replace this constructor XFile( - this.path, { + this.xPath, { this.name, int length, Uint8List bytes, - this.xPath , }) : _initBytes = bytes, _length = length, - super(path); + super(xPath); /// Constructor from XPath XFile.fromXPath( @@ -43,9 +40,8 @@ class XFile extends XFileBase { Uint8List bytes, }) : _initBytes = bytes, _length = length, - path = xPath.path, // TODO: just replace path everywhere name = xPath.name, - super(xPath.path); + super(xPath); Future get _bytes async { if (_initBytes != null) { @@ -74,4 +70,7 @@ class XFile extends XFileBase { final bytes = await _bytes; yield bytes.sublist(start ?? 0, end ?? bytes.length); } + + @override + String get path => xPath.path; } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart index 3ea492748bd1..14ae3e22a96a 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart @@ -4,13 +4,15 @@ import 'dart:typed_data'; import './base.dart'; +import '../types.dart'; + /// A XFile backed by a dart:io File. class XFile extends XFileBase { final File _file; /// Construct a XFile object backed by a dart:io File. - XFile(String path) - : _file = File(path), + XFile(XPath path) + : _file = File(path.path), super(path); @override diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/lost_data.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/lost_data.dart deleted file mode 100644 index 15bb236caece..000000000000 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/lost_data.dart +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/services.dart'; -import '../types.dart'; - -/// The response object of [ImagePicker.retrieveLostData]. -/// -/// Only applies to Android. -/// See also: -/// * [ImagePicker.retrieveLostData] for more details on retrieving lost data. -class LostData { - /// Creates an instance with the given [file], [exception], and [type]. Any of - /// the params may be null, but this is never considered to be empty. - LostData({this.file, this.exception, this.type}); - - /// Initializes an instance with all member params set to null and considered - /// to be empty. - LostData.empty() - : file = null, - exception = null, - type = null, - _empty = true; - - /// Whether it is an empty response. - /// - /// An empty response should have [file], [exception] and [type] to be null. - bool get isEmpty => _empty; - - /// The file that was lost in a previous [pickImage] or [pickVideo] call due to MainActivity being destroyed. - /// - /// Can be null if [exception] exists. - final XFile file; - - /// The exception of the last [pickImage] or [pickVideo]. - /// - /// If the last [pickImage] or [pickVideo] threw some exception before the MainActivity destruction, this variable keeps that - /// exception. - /// You should handle this exception as if the [pickImage] or [pickVideo] got an exception when the MainActivity was not destroyed. - /// - /// Note that it is not the exception that caused the destruction of the MainActivity. - final PlatformException exception; - - /// Can either be [RetrieveType.image] or [RetrieveType.video]; - final RetrieveType type; - - bool _empty = false; -} diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart index cf384f5d939b..bab59d1da68d 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart @@ -1,6 +1,7 @@ import 'dart:typed_data'; import './base.dart'; +import '../types.dart'; /// A XFile is a cross-platform, simplified File abstraction. /// @@ -15,7 +16,7 @@ class XFile extends XFileBase { /// `path` of the file doesn't match what the user sees when selecting it /// (like in web) XFile( - String path, { + XPath path, { String name, int length, Uint8List bytes, diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart index b2a614ccb304..f966a7c9a3aa 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart @@ -1,4 +1,3 @@ -export 'lost_data.dart'; export 'unsupported.dart' if (dart.library.html) 'html.dart' if (dart.library.io) 'io.dart'; From 2f5e62f437994db017c5d9691507f630f8d4d0e5 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 10 Aug 2020 15:32:28 -0700 Subject: [PATCH 085/151] Remove XPath, update API --- .../file_picker/example/lib/main.dart | 9 ++++-- .../file_picker/lib/file_picker.dart | 25 +++++---------- .../method_channel_file_picker.dart | 24 +++++++++----- .../file_picker_interface.dart | 23 +++----------- .../lib/src/types/types.dart | 7 ----- .../lib/src/types/x_file/base.dart | 6 ++-- .../lib/src/types/x_file/html.dart | 31 +++++-------------- .../lib/src/types/x_file/io.dart | 4 +-- .../lib/src/types/x_file/unsupported.dart | 3 +- .../lib/src/types/x_path/x_path.dart | 18 ----------- .../file_picker_web/lib/file_picker_web.dart | 12 +++---- 11 files changed, 53 insertions(+), 109 deletions(-) delete mode 100644 packages/file_picker/file_picker_platform_interface/lib/src/types/x_path/x_path.dart diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index 10ff2e85f0ce..b9b3e30a1e8e 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -60,13 +60,13 @@ class _MyHomePageState extends State { } void _loadFile() async { - XPath path; + XFile file; if (_extensionController.text.isNotEmpty) { List types = List(); types.add(FileTypeFilterGroup(label: 'Example Files', fileExtensions: _extensionController.text.split(','))); - path = await getReadPath(acceptedTypes: types); + file = await loadFile(acceptedTypes: types); } else { - path = await getReadPath(); + file = await loadFile(); } XFile file = XFile.fromXPath(path); @@ -137,3 +137,6 @@ class _MyHomePageState extends State { ); } } + +class XPath { +} diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index c7644ae05a11..a181ed58c65a 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -13,29 +13,20 @@ export 'package:file_picker_platform_interface/file_picker_platform_interface.da /// NEW API /// Open file dialog for loading files and return a file path -Future getReadPath({List acceptedTypes}) { +Future loadFile({List acceptedTypes}) { return FilePickerPlatform.instance.getReadPath(acceptedTypes: acceptedTypes); } /// Open file dialog for loading files and return a list of file paths -Future> getReadPaths({List acceptedTypes}) { +Future> loadFiles({List acceptedTypes}) { return FilePickerPlatform.instance.getReadPaths(acceptedTypes: acceptedTypes); } -/// Open file dialog for saving files and return a file path at which to save -Future getSavePath() { - throw UnimplementedError('loadFile() has not been implemented.'); -} - - -/// OLD API - /// Saves File to user's file system -void saveFile(Uint8List data, {String type = '', String suggestedName}) async { - return FilePickerPlatform.instance.saveFile(data, type: type, suggestedName: suggestedName); -} - -/// Loads File from user's file system -Future> loadFile({List acceptedTypes}) { - return FilePickerPlatform.instance.loadFile(acceptedTypes: acceptedTypes); +Future getSavePath( + XFile file, { + String type = '', + String suggestedName, +}) async { + return FilePickerPlatform.instance.getSavePath(data, type: type, suggestedName: suggestedName); } \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart index 3e90f4dad471..6b8925ba746b 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart @@ -13,10 +13,10 @@ const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); /// An implementation of [FilePickerPlatform] that uses method channels. class MethodChannelFilePicker extends FilePickerPlatform { - /// Load file from user's computer and return it as an XFile + /// Load a file from user's computer and return it as an XFile @override - Future> loadFile({List acceptedTypes}) { - return _channel.invokeMethod>( + Future loadFile({List acceptedTypes}) { + return _channel.invokeMethod( 'loadFile', { 'acceptedTypes': acceptedTypes, @@ -24,14 +24,24 @@ class MethodChannelFilePicker extends FilePickerPlatform { ); } + /// Load multiple files from user's computer and return it as an XFile + @override + Future> loadFiles({List acceptedTypes}) { + return _channel.invokeMethod>( + 'loadFiles', + { + 'acceptedTypes': acceptedTypes, + }, + ); + } + /// Saves the file to user's Disk @override - void saveFile(Uint8List data, {String type, String suggestedName}) async { - await _channel.invokeMethod( + Future getSavePath() async { + return _channel.invokeMethod( 'saveFile', { - 'type': type, - 'suggestedName': suggestedName, + }, ); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index 2d9fdc1178fd..ef6f9ca82b17 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -41,30 +41,17 @@ abstract class FilePickerPlatform extends PlatformInterface { } /// Open file dialog for loading files and return a file path - Future getReadPath({List acceptedTypes}) { - throw UnimplementedError('getReadPath() has not been implemented.'); - } - - /// Open file dialog for loading files and return a list of file paths - Future> getReadPaths({List acceptedTypes}) { - throw UnimplementedError('getReadPaths() has not been implemented.'); - } - - /// Open file dialog for saving files and return a file path at which to save - Future getSavePath() { + Future loadFile({List acceptedTypes}) { throw UnimplementedError('loadFile() has not been implemented.'); } - - /// OLD API: - - /// Load file from user's computer and return it as an XFile - Future> loadFile({List acceptedTypes}) { + /// Open file dialog for loading files and return a list of file paths + Future> loadFiles({List acceptedTypes}) { throw UnimplementedError('loadFile() has not been implemented.'); } - /// Saves the file to user's Disk - void saveFile(Uint8List data, {String type, String suggestedName}) async { + /// Open file dialog for saving files and return a file path at which to save + Future getSavePath() { throw UnimplementedError('saveFile() has not been implemented.'); } } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart index b8adfaa2932e..05f40e1a6adf 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart @@ -1,10 +1,3 @@ export 'x_file/x_file.dart'; -export 'x_path/x_path.dart'; export 'filter_group/filter_group.dart'; - -/// Denotes that an image is being picked. -const String kTypeImage = 'image'; - -/// Denotes that a video is being picked. -const String kTypeVideo = 'video'; diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart index 140d8f6edb12..ba3e9a57cada 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart @@ -1,8 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; -import '../types.dart'; - /// The interface for a XFile. /// /// A XFile is a container that wraps the path of a selected @@ -13,7 +11,7 @@ import '../types.dart'; /// the methods should seem familiar. abstract class XFileBase { /// Construct a XFile - XFileBase(XPath path); + XFileBase(String path); /// Get the path of the picked file. /// @@ -66,4 +64,4 @@ abstract class XFileBase { Stream openRead([int start, int end]) { throw UnimplementedError('openRead() has not been implemented.'); } -} +} \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart index 78014ff3dfcc..6284fb7a5cf7 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart @@ -5,13 +5,11 @@ import 'package:http/http.dart' as http show readBytes; import './base.dart'; -import '../types.dart'; - /// A XFile that works on web. /// /// It wraps the bytes of a selected file. class XFile extends XFileBase { - final XPath xPath; + final String path; final Uint8List _initBytes; final int _length; @override @@ -25,23 +23,13 @@ class XFile extends XFileBase { /// `name` needs to be passed from the outside, since we only have /// access to it while we create the ObjectUrl. XFile( - this.xPath, { - this.name, - int length, - Uint8List bytes, - }) : _initBytes = bytes, - _length = length, - super(xPath); - - /// Constructor from XPath - XFile.fromXPath( - this.xPath, { - int length, - Uint8List bytes, - }) : _initBytes = bytes, + this.path, { + this.name, + int length, + Uint8List bytes, + }) : _initBytes = bytes, _length = length, - name = xPath.name, - super(xPath); + super(path); Future get _bytes async { if (_initBytes != null) { @@ -70,7 +58,4 @@ class XFile extends XFileBase { final bytes = await _bytes; yield bytes.sublist(start ?? 0, end ?? bytes.length); } - - @override - String get path => xPath.path; -} +} \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart index 14ae3e22a96a..acc3986975d3 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart @@ -11,8 +11,8 @@ class XFile extends XFileBase { final File _file; /// Construct a XFile object backed by a dart:io File. - XFile(XPath path) - : _file = File(path.path), + XFile(String path) + : _file = File(path), super(path); @override diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart index bab59d1da68d..cf384f5d939b 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart @@ -1,7 +1,6 @@ import 'dart:typed_data'; import './base.dart'; -import '../types.dart'; /// A XFile is a cross-platform, simplified File abstraction. /// @@ -16,7 +15,7 @@ class XFile extends XFileBase { /// `path` of the file doesn't match what the user sees when selecting it /// (like in web) XFile( - XPath path, { + String path, { String name, int length, Uint8List bytes, diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_path/x_path.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_path/x_path.dart deleted file mode 100644 index 07df81e9dce8..000000000000 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_path/x_path.dart +++ /dev/null @@ -1,18 +0,0 @@ -/// Cross platform path class -class XPath { - /// XPath constructor - XPath(this._path, {String name, int modified, int created}): - _modified = modified, - _created = created, - _name = name; - - final String _path; - final String _name; - final int _modified; - final int _created; - - String get path => _path; - int get modified => _modified; - int get created => _created; - String get name => _name; -} \ No newline at end of file diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 74e8e5595518..18695225fbc7 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -169,7 +169,7 @@ class FilePickerPlugin extends FilePickerPlatform { /// Open file dialog for loading files and return a file path @override - Future getReadPath({List acceptedTypes}) { + Future loadFile({List acceptedTypes}) { Completer _completer = Completer(); _readPathHelper(false, acceptedTypes).then((list) { _completer.complete(list.first); @@ -183,18 +183,14 @@ class FilePickerPlugin extends FilePickerPlatform { /// Open file dialog for loading files and return a list of file paths @override - Future> getReadPaths({List acceptedTypes}) { + Future> loadFiles({List acceptedTypes}) { return _readPathHelper(true, acceptedTypes); } - - /// Open file dialog for saving files and return a file path at which to save + @override - Future getSavePath() { - return Future.value(); - } + Future getSavePath() => Future.value(); /// Web implementation of saveFile() - @override void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { // Create blob from data final blob = createBlob(data, type); From 7b2237e4d57339c84d145b2a0787e608990c6915 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 10 Aug 2020 17:04:54 -0700 Subject: [PATCH 086/151] Move Save Logic to XFile and Remove XPath new fromData constructor in XFile class --- .../file_picker/example/lib/main.dart | 9 +-- .../file_picker/lib/file_picker.dart | 12 ++-- .../lib/src/types/x_file/base.dart | 5 ++ .../lib/src/types/x_file/html.dart | 72 +++++++++++++++++-- .../lib/src/types/x_file/io.dart | 11 +++ .../lib/src/types/x_file/unsupported.dart | 11 +++ .../file_picker_web/lib/file_picker_web.dart | 60 +++++----------- 7 files changed, 119 insertions(+), 61 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index b9b3e30a1e8e..2790544fada8 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -51,12 +51,15 @@ class _MyHomePageState extends State { Uint8List data; data = Uint8List.fromList(_fileController.text.codeUnits); + XFile new_file; // await? if (_nameController.text == '') { - saveFile(data, type: 'text/plain'); + new_file = XFile.fromData(data); } else { - saveFile(data, type: 'text/plain', suggestedName: _nameController.text); + new_file = XFile.fromData(data, name: _nameController.text); } + + new_file.saveTo(''); } void _loadFile() async { @@ -69,8 +72,6 @@ class _MyHomePageState extends State { file = await loadFile(); } - XFile file = XFile.fromXPath(path); - String text = await file.readAsString(); _fileController.text = text; diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index a181ed58c65a..a1e5743f7382 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -14,19 +14,15 @@ export 'package:file_picker_platform_interface/file_picker_platform_interface.da /// Open file dialog for loading files and return a file path Future loadFile({List acceptedTypes}) { - return FilePickerPlatform.instance.getReadPath(acceptedTypes: acceptedTypes); + return FilePickerPlatform.instance.loadFile(acceptedTypes: acceptedTypes); } /// Open file dialog for loading files and return a list of file paths Future> loadFiles({List acceptedTypes}) { - return FilePickerPlatform.instance.getReadPaths(acceptedTypes: acceptedTypes); + return FilePickerPlatform.instance.loadFiles(acceptedTypes: acceptedTypes); } /// Saves File to user's file system -Future getSavePath( - XFile file, { - String type = '', - String suggestedName, -}) async { - return FilePickerPlatform.instance.getSavePath(data, type: type, suggestedName: suggestedName); +Future getSavePath() async { + return FilePickerPlatform.instance.getSavePath(); } \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart index ba3e9a57cada..4ea55b864cb7 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart @@ -13,6 +13,11 @@ abstract class XFileBase { /// Construct a XFile XFileBase(String path); + /// Save the XFile at the indicated file path. + void saveTo(String path) { + throw UnimplementedError('saveTo has not been implemented.'); + } + /// Get the path of the picked file. /// /// This should only be used as a backwards-compatibility clutch diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart index 6284fb7a5cf7..6f0e3f39dfce 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart @@ -2,6 +2,8 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:http/http.dart' as http show readBytes; +import 'package:meta/meta.dart'; +import 'dart:html'; import './base.dart'; @@ -9,11 +11,12 @@ import './base.dart'; /// /// It wraps the bytes of a selected file. class XFile extends XFileBase { - final String path; - final Uint8List _initBytes; + String path; + final Uint8List _data; final int _length; @override final String name; + Element _target; /// Construct a XFile object from its ObjectUrl. /// @@ -27,13 +30,32 @@ class XFile extends XFileBase { this.name, int length, Uint8List bytes, - }) : _initBytes = bytes, + }) : _data = bytes, _length = length, - super(path); + super(path) { + // Create a DOM container where we can host the anchor. + _target = _ensureInitialized(this.name + '-x-file-dom-element'); + } + + /// Construct an XFile from its data + XFile.fromData( + Uint8List bytes, { + this.name, + int length, + }) : _data = bytes, + _length = length, + super('') { + Blob blob = Blob([bytes]); + this.path = Url.createObjectUrl(blob); + // Create a DOM container where we can host the anchor. + _target = _ensureInitialized(this.name + '-x-file-dom-element'); + } + + Future get _bytes async { - if (_initBytes != null) { - return Future.value(UnmodifiableUint8ListView(_initBytes)); + if (_data != null) { + return Future.value(UnmodifiableUint8ListView(_data)); } return http.readBytes(path); } @@ -58,4 +80,42 @@ class XFile extends XFileBase { final bytes = await _bytes; yield bytes.sublist(start ?? 0, end ?? bytes.length); } + + /// Saves the data of this XFile at the location indicated by path. + /// For the web implementation, the path variable is ignored. + void saveTo(String path) { + // Create an tag with the appropriate download attributes and click it + final AnchorElement element = createAnchorElement(this.path, this.name); + + _addElementToDomAndClick(element); + } + + /// Create anchor element with download attribute + @visibleForTesting + AnchorElement createAnchorElement(String href, String suggestedName) { + final element = AnchorElement(href: href); + element.download = suggestedName; + return element; + } + + void _addElementToDomAndClick(Element element) { + // Add the file input element and click it + // All previous elements will be removed before adding the new one + _target.children.clear(); + _target.children.add(element); + element.click(); + } + + /// Initializes a DOM container where we can host elements. + Element _ensureInitialized(String id) { + var target = querySelector('#${id}'); + if (target == null) { + final Element targetElement = + Element.tag('flt-x-file-input')..id = id; + + querySelector('body').children.add(targetElement); + target = targetElement; + } + return target; + } } \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart index acc3986975d3..9966ab39bbb4 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart @@ -9,10 +9,21 @@ import '../types.dart'; /// A XFile backed by a dart:io File. class XFile extends XFileBase { final File _file; + final Uint8List _data; /// Construct a XFile object backed by a dart:io File. XFile(String path) : _file = File(path), + _data = null, + super(path); + + /// Construct an XFile from its data + XFile.fromData(Uint8List data, { + String path, + String name, + int length, + }): _data = data, + _file = File(path), super(path); @override diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart index cf384f5d939b..8acdeb329b54 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart @@ -23,4 +23,15 @@ class XFile extends XFileBase { throw UnimplementedError( 'XFile is not available in your current platform.'); } + + /// Construct a XFile object from its data + XFile.fromData( + Uint8List data, { + String path, + String name, + int length, + }) : super(path) { + throw UnimplementedError( + 'XFile is not available in your current platform.'); + } } diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 18695225fbc7..242af28b66ad 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -63,8 +63,8 @@ class FilePickerPlugin extends FilePickerPlatform { element.click(); } - List _getXPathsFromFiles (List files) { - List xPaths = List(); + List _getXFilesFromFiles (List files) { + List xFiles = List(); for (File file in files) { String url = Url.createObjectUrl(file); @@ -72,10 +72,10 @@ class FilePickerPlugin extends FilePickerPlatform { int length = file.size; int modified = file.lastModified; - xPaths.add(XPath(url, name: name, modified: modified)); + xFiles.add(XFile(url, name: name)); } - return xPaths; + return xFiles; } /// Getter for retrieving files from an input element @@ -88,8 +88,8 @@ class FilePickerPlugin extends FilePickerPlatform { return element?.files ?? []; } - Future _getFileWhenReady(InputElement element) { - final Completer _completer = Completer(); + Future _getFileWhenReady(InputElement element) { + final Completer _completer = Completer(); _getFilesWhenReady(element) .then((list) { @@ -104,16 +104,16 @@ class FilePickerPlugin extends FilePickerPlatform { /// Listen for file input element to change and retrieve files when /// this happens. - Future> _getFilesWhenReady(InputElement element) { - final Completer> _completer = Completer(); + Future> _getFilesWhenReady(InputElement element) { + final Completer> _completer = Completer(); // Listens for element change element.onChange.first.then((event) { // File type from dart:html class final List files = getFilesFromInputElement(element); - // Create XPath from dart:html Files - final returnPaths = _getXPathsFromFiles(files); + // Create XFile from dart:html Files + final returnPaths = _getXFilesFromFiles(files); _completer.complete(returnPaths); }); @@ -127,20 +127,6 @@ class FilePickerPlugin extends FilePickerPlatform { return _completer.future; } - /// Create anchor element with download attribute - @visibleForTesting - AnchorElement createAnchorElement(String href, String suggestedName) { - final element = AnchorElement(href: href); - element.download = suggestedName; - return element; - } - - /// Create blob with specified data of indicated type - @visibleForTesting - Blob createBlob(Uint8List data, String type) { - return type.isEmpty ? Blob([data]) : Blob([data], type); - } - /// Initializes a DOM container where we can host elements. Element _ensureInitialized(String id) { var target = querySelector('#${id}'); @@ -156,8 +142,8 @@ class FilePickerPlugin extends FilePickerPlatform { /// NEW API - // Load Helper - Future> _readPathHelper (bool multiple, List acceptedTypes) { + /// Load Helper + Future> _loadFileHelper (bool multiple, List acceptedTypes) { final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); final FileUploadInputElement element = createFileInputElement(acceptedTypeString, multiple); @@ -169,9 +155,9 @@ class FilePickerPlugin extends FilePickerPlatform { /// Open file dialog for loading files and return a file path @override - Future loadFile({List acceptedTypes}) { - Completer _completer = Completer(); - _readPathHelper(false, acceptedTypes).then((list) { + Future loadFile({List acceptedTypes}) { + Completer _completer = Completer(); + _loadFileHelper(false, acceptedTypes).then((list) { _completer.complete(list.first); }) .catchError((err) { @@ -183,24 +169,12 @@ class FilePickerPlugin extends FilePickerPlatform { /// Open file dialog for loading files and return a list of file paths @override - Future> loadFiles({List acceptedTypes}) { - return _readPathHelper(true, acceptedTypes); + Future> loadFiles({List acceptedTypes}) { + return _loadFileHelper(true, acceptedTypes); } @override Future getSavePath() => Future.value(); - - /// Web implementation of saveFile() - void saveFile(Uint8List data, {String type = '', String suggestedName = ''}) async { - // Create blob from data - final blob = createBlob(data, type); - String url = Url.createObjectUrl(blob); - - // Create an tag with the appropriate download attributes and click it - final AnchorElement element = createAnchorElement(url, suggestedName); - - _addElementToDomAndClick(element); - } } /// Overrides some functions to allow testing From 15e9252c25895d20ed5e6821a2a9ed8dcd8a7007 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 17 Aug 2020 15:34:58 -0700 Subject: [PATCH 087/151] Add XType and XTypeGroup --- .../file_picker/example/lib/main.dart | 10 ++-- .../file_picker/lib/file_picker.dart | 10 ++-- .../method_channel_file_picker.dart | 8 ++-- .../file_picker_interface.dart | 4 +- .../src/types/filter_group/filter_group.dart | 47 +++++++++++++++++-- .../lib/src/types/x_file/x_file.dart | 2 +- .../pubspec.yaml | 1 + .../file_picker_web/lib/file_picker_web.dart | 18 +++---- 8 files changed, 73 insertions(+), 27 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index 2790544fada8..b7a10fcc8da6 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -65,9 +65,13 @@ class _MyHomePageState extends State { void _loadFile() async { XFile file; if (_extensionController.text.isNotEmpty) { - List types = List(); - types.add(FileTypeFilterGroup(label: 'Example Files', fileExtensions: _extensionController.text.split(','))); - file = await loadFile(acceptedTypes: types); + List typeGroups = List(); + + List types = List(); + _extensionController.text.split(',').forEach((type) => types.add(XType.fromExtension(type))); + + typeGroups.add(XTypeGroup(label: 'Example Files', fileTypes: types)); + file = await loadFile(acceptedTypeGroups: typeGroups); } else { file = await loadFile(); } diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_picker/file_picker/lib/file_picker.dart index a1e5743f7382..0857ecc11bb6 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_picker/file_picker/lib/file_picker.dart @@ -8,18 +8,18 @@ import 'dart:typed_data'; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; export 'package:file_picker_platform_interface/file_picker_platform_interface.dart' - show XFile, FileTypeFilterGroup, XPath; + show XFile, XTypeGroup, XType; /// NEW API /// Open file dialog for loading files and return a file path -Future loadFile({List acceptedTypes}) { - return FilePickerPlatform.instance.loadFile(acceptedTypes: acceptedTypes); +Future loadFile({List acceptedTypeGroups}) { + return FilePickerPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups); } /// Open file dialog for loading files and return a list of file paths -Future> loadFiles({List acceptedTypes}) { - return FilePickerPlatform.instance.loadFiles(acceptedTypes: acceptedTypes); +Future> loadFiles({List acceptedTypeGroups}) { + return FilePickerPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups); } /// Saves File to user's file system diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart index 6b8925ba746b..5cdcf5d86fdb 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart @@ -15,22 +15,22 @@ const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); class MethodChannelFilePicker extends FilePickerPlatform { /// Load a file from user's computer and return it as an XFile @override - Future loadFile({List acceptedTypes}) { + Future loadFile({List acceptedTypeGroups}) { return _channel.invokeMethod( 'loadFile', { - 'acceptedTypes': acceptedTypes, + 'acceptedTypes': acceptedTypeGroups, }, ); } /// Load multiple files from user's computer and return it as an XFile @override - Future> loadFiles({List acceptedTypes}) { + Future> loadFiles({List acceptedTypeGroups}) { return _channel.invokeMethod>( 'loadFiles', { - 'acceptedTypes': acceptedTypes, + 'acceptedTypes': acceptedTypeGroups, }, ); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart index ef6f9ca82b17..260def18f868 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart @@ -41,12 +41,12 @@ abstract class FilePickerPlatform extends PlatformInterface { } /// Open file dialog for loading files and return a file path - Future loadFile({List acceptedTypes}) { + Future loadFile({List acceptedTypeGroups}) { throw UnimplementedError('loadFile() has not been implemented.'); } /// Open file dialog for loading files and return a list of file paths - Future> loadFiles({List acceptedTypes}) { + Future> loadFiles({List acceptedTypeGroups}) { throw UnimplementedError('loadFile() has not been implemented.'); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart index 186e1eca42b3..f79c0c679e32 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart @@ -12,10 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// A set of allowed file types. -class FileTypeFilterGroup { +// TODO: should we be using this package or just extracting the important conversion data from it? +import 'package:mime_type/mime_type.dart'; + +/// A set of allowed XTypes +class XTypeGroup { /// Creates a new group with the given label and file extensions. - const FileTypeFilterGroup({this.label, this.fileExtensions}); + const XTypeGroup({this.label, this.fileTypes}); /// The label for the grouping. On platforms that support selectable groups, /// this will be visible to the user for selecting the group. @@ -24,5 +27,41 @@ class FileTypeFilterGroup { /// A list of allowed file extensions. E.g., ['png', 'jpg', 'jpeg', 'gif']. /// /// A null or empty list indicates any type is allowed. - final List fileExtensions; + final List fileTypes; } + +/// A cross platform file type +class XType { + /// Variables to store type + String _mime; + String _extension; + + /// Default constructor + XType({ + String extension, + String mime, + }) : this._extension = extension, + this._mime = mime; + + /// Constructors that take other files types as input + XType.fromMime(String mime) : this._mime = mime; + + /// Constructor that takes extension as input + XType.fromExtension(String extension) : this._extension = extension; + + /// Get the mime type from this XType + String get mime { + if (mime == null) { + _mime = mimeFromExtension(_extension); + } + return _mime; + } + + /// Get the extension from this XType + String get extension { + if (_extension == null) { + _extension = extensionFromMime(_mime); + } + return _extension; + } +} \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart index f966a7c9a3aa..72d156cc58fd 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart @@ -1,3 +1,3 @@ export 'unsupported.dart' if (dart.library.html) 'html.dart' - if (dart.library.io) 'io.dart'; + if (dart.library.io) 'io.dart'; \ No newline at end of file diff --git a/packages/file_picker/file_picker_platform_interface/pubspec.yaml b/packages/file_picker/file_picker_platform_interface/pubspec.yaml index 249d9f1f9a89..9b8de97e38a6 100644 --- a/packages/file_picker/file_picker_platform_interface/pubspec.yaml +++ b/packages/file_picker/file_picker_platform_interface/pubspec.yaml @@ -11,6 +11,7 @@ dependencies: meta: ^1.0.5 http: ^0.12.0+1 plugin_platform_interface: ^1.0.1 + mime_type: ^0.3.1 dev_dependencies: test: ^1.15.0 diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index 242af28b66ad..f99d44c3717d 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -31,10 +31,12 @@ class FilePickerPlugin extends FilePickerPlatform { } /// Convert list of filter groups to a comma-separated string - String _getStringFromFilterGroup (List acceptedTypes) { + String _getStringFromFilterGroup (List acceptedTypes) { List allExtensions = List(); - for (FileTypeFilterGroup group in acceptedTypes ?? []) { - allExtensions += group.fileExtensions; + for (XTypeGroup group in acceptedTypes ?? []) { + for (XType type in group.fileTypes ?? []) { + allExtensions.add(type.extension); + } } return allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; } @@ -143,7 +145,7 @@ class FilePickerPlugin extends FilePickerPlatform { /// NEW API /// Load Helper - Future> _loadFileHelper (bool multiple, List acceptedTypes) { + Future> _loadFileHelper (bool multiple, List acceptedTypes) { final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); final FileUploadInputElement element = createFileInputElement(acceptedTypeString, multiple); @@ -155,9 +157,9 @@ class FilePickerPlugin extends FilePickerPlatform { /// Open file dialog for loading files and return a file path @override - Future loadFile({List acceptedTypes}) { + Future loadFile({List acceptedTypeGroups}) { Completer _completer = Completer(); - _loadFileHelper(false, acceptedTypes).then((list) { + _loadFileHelper(false, acceptedTypeGroups).then((list) { _completer.complete(list.first); }) .catchError((err) { @@ -169,8 +171,8 @@ class FilePickerPlugin extends FilePickerPlatform { /// Open file dialog for loading files and return a list of file paths @override - Future> loadFiles({List acceptedTypes}) { - return _loadFileHelper(true, acceptedTypes); + Future> loadFiles({List acceptedTypeGroups}) { + return _loadFileHelper(true, acceptedTypeGroups); } @override From 0aa471c8d19450944b7bb66a9bccb13a83d7e96b Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 17 Aug 2020 16:57:33 -0700 Subject: [PATCH 088/151] Remove Named Constructors --- packages/file_picker/file_picker/example/lib/main.dart | 2 +- .../lib/src/types/filter_group/filter_group.dart | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index b7a10fcc8da6..e98e5d99fbc9 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -68,7 +68,7 @@ class _MyHomePageState extends State { List typeGroups = List(); List types = List(); - _extensionController.text.split(',').forEach((type) => types.add(XType.fromExtension(type))); + _extensionController.text.split(',').forEach((type) => types.add(XType(extension: type))); typeGroups.add(XTypeGroup(label: 'Example Files', fileTypes: types)); file = await loadFile(acceptedTypeGroups: typeGroups); diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart index f79c0c679e32..76d0343d2c87 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart @@ -43,12 +43,6 @@ class XType { }) : this._extension = extension, this._mime = mime; - /// Constructors that take other files types as input - XType.fromMime(String mime) : this._mime = mime; - - /// Constructor that takes extension as input - XType.fromExtension(String extension) : this._extension = extension; - /// Get the mime type from this XType String get mime { if (mime == null) { From 48adaee3e2f2afed6b75388b4bc4b8d26e038ea0 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 17 Aug 2020 17:14:50 -0700 Subject: [PATCH 089/151] Bug Fix, 'accept' attribute requires extensions start with '.' --- .../file_picker/file_picker_web/lib/file_picker_web.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_picker/file_picker_web/lib/file_picker_web.dart index f99d44c3717d..a9f58bbf0f54 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_picker/file_picker_web/lib/file_picker_web.dart @@ -35,7 +35,10 @@ class FilePickerPlugin extends FilePickerPlatform { List allExtensions = List(); for (XTypeGroup group in acceptedTypes ?? []) { for (XType type in group.fileTypes ?? []) { - allExtensions.add(type.extension); + if (type.extension == null) { + continue; + } + allExtensions.add('.' + type.extension); } } return allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; From 6a586dcd1251745467208cc4305cd4e073a7a04d Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 18 Aug 2020 16:34:34 -0700 Subject: [PATCH 090/151] Rename Filter Group to XType --- .../file_picker_platform_interface/lib/src/types/types.dart | 2 +- .../{filter_group/filter_group.dart => x_type/x_type.dart} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename packages/file_picker/file_picker_platform_interface/lib/src/types/{filter_group/filter_group.dart => x_type/x_type.dart} (100%) diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart index 05f40e1a6adf..cae9d41efedf 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart @@ -1,3 +1,3 @@ export 'x_file/x_file.dart'; -export 'filter_group/filter_group.dart'; +export 'x_type/x_type.dart'; diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_type/x_type.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/filter_group/filter_group.dart rename to packages/file_picker/file_picker_platform_interface/lib/src/types/x_type/x_type.dart From 6b3e74943b5dabe608c7e0c1e913da7fdbd02e55 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 18 Aug 2020 17:30:38 -0700 Subject: [PATCH 091/151] Add XType to XFile and Update XFile small bug fix in XType mime to _mime (infinite loop) --- .../file_picker/example/lib/main.dart | 7 ++-- .../lib/src/types/x_file/base.dart | 3 +- .../lib/src/types/x_file/html.dart | 16 ++++++-- .../lib/src/types/x_file/io.dart | 38 +++++++++++++++++-- .../lib/src/types/x_type/x_type.dart | 2 +- 5 files changed, 54 insertions(+), 12 deletions(-) diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_picker/file_picker/example/lib/main.dart index e98e5d99fbc9..78245d400fe0 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_picker/file_picker/example/lib/main.dart @@ -52,11 +52,12 @@ class _MyHomePageState extends State { data = Uint8List.fromList(_fileController.text.codeUnits); XFile new_file; - // await? + XType type = XType(extension: '.txt'); + if (_nameController.text == '') { - new_file = XFile.fromData(data); + new_file = XFile.fromData(data, type: type); } else { - new_file = XFile.fromData(data, name: _nameController.text); + new_file = XFile.fromData(data, type: type, name: _nameController.text); } new_file.saveTo(''); diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart index 4ea55b864cb7..1b5c81f9bde0 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; +import '../x_type/x_type.dart'; /// The interface for a XFile. /// @@ -14,7 +15,7 @@ abstract class XFileBase { XFileBase(String path); /// Save the XFile at the indicated file path. - void saveTo(String path) { + void saveTo(String path) async { throw UnimplementedError('saveTo has not been implemented.'); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart index 6f0e3f39dfce..c1cc415c739f 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart @@ -7,14 +7,17 @@ import 'dart:html'; import './base.dart'; +import '../x_type/x_type.dart'; + /// A XFile that works on web. /// /// It wraps the bytes of a selected file. class XFile extends XFileBase { String path; + final XType type; + final Uint8List _data; final int _length; - @override final String name; Element _target; @@ -27,6 +30,7 @@ class XFile extends XFileBase { /// access to it while we create the ObjectUrl. XFile( this.path, { + this.type, this.name, int length, Uint8List bytes, @@ -40,12 +44,18 @@ class XFile extends XFileBase { /// Construct an XFile from its data XFile.fromData( Uint8List bytes, { + this.type, this.name, int length, }) : _data = bytes, _length = length, super('') { - Blob blob = Blob([bytes]); + Blob blob; + if (type.mime == null) { + blob = Blob([bytes]); + } else { + blob = Blob([bytes], type.mime); + } this.path = Url.createObjectUrl(blob); // Create a DOM container where we can host the anchor. _target = _ensureInitialized(this.name + '-x-file-dom-element'); @@ -83,7 +93,7 @@ class XFile extends XFileBase { /// Saves the data of this XFile at the location indicated by path. /// For the web implementation, the path variable is ignored. - void saveTo(String path) { + void saveTo(String path) async { // Create an tag with the appropriate download attributes and click it final AnchorElement element = createAnchorElement(this.path, this.name); diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart index 9966ab39bbb4..3538e6364804 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart @@ -9,22 +9,39 @@ import '../types.dart'; /// A XFile backed by a dart:io File. class XFile extends XFileBase { final File _file; + int _length; + final Uint8List _data; + final XType type; + /// Construct a XFile object backed by a dart:io File. - XFile(String path) + XFile(String path, { this.type }) : _file = File(path), _data = null, super(path); /// Construct an XFile from its data XFile.fromData(Uint8List data, { + this.type, String path, String name, int length, }): _data = data, _file = File(path), - super(path); + _length = length, + super(path) { + if (length == null) { + _length = data.length; + } + } + + @override + void saveTo(String path) async { + File fileToSave = File(path); + await fileToSave.writeAsBytes(_data); + await fileToSave.create(); + } @override String get path { @@ -38,22 +55,35 @@ class XFile extends XFileBase { @override Future length() { + if (_length != null) { + return Future.value(_length); + } return _file.length(); } @override Future readAsString({Encoding encoding = utf8}) { + if (_data != null) { + return Future.value(String.fromCharCodes(_data)); + } return _file.readAsString(encoding: encoding); } @override Future readAsBytes() { + if (_data != null) { + return Future.value(_data); + } return _file.readAsBytes(); } @override - Stream openRead([int start, int end]) { - return _file + Stream openRead([int start, int end]) async* { + if (_data != null) { + final bytes = _data; + yield bytes.sublist(start ?? 0, end ?? bytes.length); + } + yield* _file .openRead(start ?? 0, end) .map((chunk) => Uint8List.fromList(chunk)); } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_type/x_type.dart b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_type/x_type.dart index 76d0343d2c87..ace2f602d45b 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_type/x_type.dart +++ b/packages/file_picker/file_picker_platform_interface/lib/src/types/x_type/x_type.dart @@ -45,7 +45,7 @@ class XType { /// Get the mime type from this XType String get mime { - if (mime == null) { + if (_mime == null) { _mime = mimeFromExtension(_extension); } return _mime; From 62ddfdfa6dacc462e983a52373600144fb0b895a Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 18 Aug 2020 17:48:51 -0700 Subject: [PATCH 092/151] Refactor file_picker to file_selector --- .../lib/file_picker_platform_interface.dart | 2 -- .../file_selector}/CHANGELOG.md | 0 .../file_selector}/LICENSE | 0 .../file_selector}/README.md | 0 .../file_selector}/example/.gitignore | 0 .../file_selector}/example/.metadata | 0 .../file_selector}/example/README.md | 0 .../file_selector}/example/lib/main.dart | 2 +- .../file_selector}/example/pubspec.yaml | 2 +- .../example/test/widget_test.dart | 0 .../file_selector}/example/web/favicon.png | Bin .../example/web/icons/Icon-192.png | Bin .../example/web/icons/Icon-512.png | Bin .../file_selector}/example/web/index.html | 0 .../file_selector}/example/web/manifest.json | 0 .../file_selector/lib/file_selector.dart} | 10 ++++---- .../file_selector}/pubspec.yaml | 12 ++++----- .../file_selector}/test/file_picker_test.dart | 0 .../CHANGELOG.md | 0 .../file_selector_platform_interface}/LICENSE | 0 .../README.md | 0 .../lib/file_selector_platform_interface.dart | 2 ++ .../method_channel_file_selector.dart} | 8 +++--- .../file_selector_interface.dart} | 24 +++++++++--------- .../lib/src/types/types.dart | 0 .../lib/src/types/x_file/base.dart | 0 .../lib/src/types/x_file/html.dart | 0 .../lib/src/types/x_file/io.dart | 0 .../lib/src/types/x_file/unsupported.dart | 0 .../lib/src/types/x_file/x_file.dart | 0 .../lib/src/types/x_type/x_type.dart | 0 .../pubspec.yaml | 2 +- .../file_picker_platform_interface_test.dart | 4 +-- .../file_selector_web}/CHANGELOG.md | 0 .../file_selector_web}/LICENSE | 0 .../file_selector_web}/README.md | 0 .../lib/file_selector_web.dart} | 24 +++++++++--------- .../file_selector_web}/pubspec.yaml | 14 +++++----- .../file_selector_web}/test/README.md | 0 .../file_selector_web}/test/lib/main.dart | 0 .../file_selector_web}/test/pubspec.yaml | 0 .../test/test_driver/web_e2e.dart | 2 +- .../test/test_driver/web_e2e_test.dart | 0 .../file_selector_web}/test/web/index.html | 0 44 files changed, 54 insertions(+), 54 deletions(-) delete mode 100644 packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart rename packages/{file_picker/file_picker => file_selector/file_selector}/CHANGELOG.md (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/LICENSE (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/README.md (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/.gitignore (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/.metadata (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/README.md (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/lib/main.dart (98%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/pubspec.yaml (99%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/test/widget_test.dart (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/web/favicon.png (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/web/icons/Icon-192.png (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/web/icons/Icon-512.png (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/web/index.html (100%) rename packages/{file_picker/file_picker => file_selector/file_selector}/example/web/manifest.json (100%) rename packages/{file_picker/file_picker/lib/file_picker.dart => file_selector/file_selector/lib/file_selector.dart} (59%) rename packages/{file_picker/file_picker => file_selector/file_selector}/pubspec.yaml (72%) rename packages/{file_picker/file_picker => file_selector/file_selector}/test/file_picker_test.dart (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/CHANGELOG.md (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/LICENSE (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/README.md (100%) create mode 100644 packages/file_selector/file_selector_platform_interface/lib/file_selector_platform_interface.dart rename packages/{file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart => file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart} (79%) rename packages/{file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart => file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart} (68%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/lib/src/types/types.dart (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/lib/src/types/x_file/base.dart (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/lib/src/types/x_file/html.dart (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/lib/src/types/x_file/io.dart (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/lib/src/types/x_file/unsupported.dart (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/lib/src/types/x_file/x_file.dart (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/lib/src/types/x_type/x_type.dart (100%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/pubspec.yaml (94%) rename packages/{file_picker/file_picker_platform_interface => file_selector/file_selector_platform_interface}/test/file_picker_platform_interface_test.dart (91%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/CHANGELOG.md (100%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/LICENSE (100%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/README.md (100%) rename packages/{file_picker/file_picker_web/lib/file_picker_web.dart => file_selector/file_selector_web/lib/file_selector_web.dart} (87%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/pubspec.yaml (68%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/test/README.md (100%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/test/lib/main.dart (100%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/test/pubspec.yaml (100%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/test/test_driver/web_e2e.dart (98%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/test/test_driver/web_e2e_test.dart (100%) rename packages/{file_picker/file_picker_web => file_selector/file_selector_web}/test/web/index.html (100%) diff --git a/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart b/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart deleted file mode 100644 index f851916ca9e4..000000000000 --- a/packages/file_picker/file_picker_platform_interface/lib/file_picker_platform_interface.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'src/platform_interface/file_picker_interface.dart'; -export 'src/types/types.dart'; diff --git a/packages/file_picker/file_picker/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md similarity index 100% rename from packages/file_picker/file_picker/CHANGELOG.md rename to packages/file_selector/file_selector/CHANGELOG.md diff --git a/packages/file_picker/file_picker/LICENSE b/packages/file_selector/file_selector/LICENSE similarity index 100% rename from packages/file_picker/file_picker/LICENSE rename to packages/file_selector/file_selector/LICENSE diff --git a/packages/file_picker/file_picker/README.md b/packages/file_selector/file_selector/README.md similarity index 100% rename from packages/file_picker/file_picker/README.md rename to packages/file_selector/file_selector/README.md diff --git a/packages/file_picker/file_picker/example/.gitignore b/packages/file_selector/file_selector/example/.gitignore similarity index 100% rename from packages/file_picker/file_picker/example/.gitignore rename to packages/file_selector/file_selector/example/.gitignore diff --git a/packages/file_picker/file_picker/example/.metadata b/packages/file_selector/file_selector/example/.metadata similarity index 100% rename from packages/file_picker/file_picker/example/.metadata rename to packages/file_selector/file_selector/example/.metadata diff --git a/packages/file_picker/file_picker/example/README.md b/packages/file_selector/file_selector/example/README.md similarity index 100% rename from packages/file_picker/file_picker/example/README.md rename to packages/file_selector/file_selector/example/README.md diff --git a/packages/file_picker/file_picker/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart similarity index 98% rename from packages/file_picker/file_picker/example/lib/main.dart rename to packages/file_selector/file_selector/example/lib/main.dart index 78245d400fe0..2ae04cfb2b4e 100644 --- a/packages/file_picker/file_picker/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -1,7 +1,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; -import 'package:file_picker/file_picker.dart'; +import 'package:file_selector/file_selector.dart'; void main() { runApp(MyApp()); diff --git a/packages/file_picker/file_picker/example/pubspec.yaml b/packages/file_selector/file_selector/example/pubspec.yaml similarity index 99% rename from packages/file_picker/file_picker/example/pubspec.yaml rename to packages/file_selector/file_selector/example/pubspec.yaml index c292d1b300aa..58f0abbf2658 100644 --- a/packages/file_picker/file_picker/example/pubspec.yaml +++ b/packages/file_selector/file_selector/example/pubspec.yaml @@ -24,7 +24,7 @@ dependencies: flutter: sdk: flutter - file_picker: + file_selector: path: ../ # The following adds the Cupertino Icons font to your application. diff --git a/packages/file_picker/file_picker/example/test/widget_test.dart b/packages/file_selector/file_selector/example/test/widget_test.dart similarity index 100% rename from packages/file_picker/file_picker/example/test/widget_test.dart rename to packages/file_selector/file_selector/example/test/widget_test.dart diff --git a/packages/file_picker/file_picker/example/web/favicon.png b/packages/file_selector/file_selector/example/web/favicon.png similarity index 100% rename from packages/file_picker/file_picker/example/web/favicon.png rename to packages/file_selector/file_selector/example/web/favicon.png diff --git a/packages/file_picker/file_picker/example/web/icons/Icon-192.png b/packages/file_selector/file_selector/example/web/icons/Icon-192.png similarity index 100% rename from packages/file_picker/file_picker/example/web/icons/Icon-192.png rename to packages/file_selector/file_selector/example/web/icons/Icon-192.png diff --git a/packages/file_picker/file_picker/example/web/icons/Icon-512.png b/packages/file_selector/file_selector/example/web/icons/Icon-512.png similarity index 100% rename from packages/file_picker/file_picker/example/web/icons/Icon-512.png rename to packages/file_selector/file_selector/example/web/icons/Icon-512.png diff --git a/packages/file_picker/file_picker/example/web/index.html b/packages/file_selector/file_selector/example/web/index.html similarity index 100% rename from packages/file_picker/file_picker/example/web/index.html rename to packages/file_selector/file_selector/example/web/index.html diff --git a/packages/file_picker/file_picker/example/web/manifest.json b/packages/file_selector/file_selector/example/web/manifest.json similarity index 100% rename from packages/file_picker/file_picker/example/web/manifest.json rename to packages/file_selector/file_selector/example/web/manifest.json diff --git a/packages/file_picker/file_picker/lib/file_picker.dart b/packages/file_selector/file_selector/lib/file_selector.dart similarity index 59% rename from packages/file_picker/file_picker/lib/file_picker.dart rename to packages/file_selector/file_selector/lib/file_selector.dart index 0857ecc11bb6..26b5f5404329 100644 --- a/packages/file_picker/file_picker/lib/file_picker.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -5,24 +5,24 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; -export 'package:file_picker_platform_interface/file_picker_platform_interface.dart' +export 'package:file_selector_platform_interface/file_selector_platform_interface.dart' show XFile, XTypeGroup, XType; /// NEW API /// Open file dialog for loading files and return a file path Future loadFile({List acceptedTypeGroups}) { - return FilePickerPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups); + return FileSelectorPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups); } /// Open file dialog for loading files and return a list of file paths Future> loadFiles({List acceptedTypeGroups}) { - return FilePickerPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups); + return FileSelectorPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups); } /// Saves File to user's file system Future getSavePath() async { - return FilePickerPlatform.instance.getSavePath(); + return FileSelectorPlatform.instance.getSavePath(); } \ No newline at end of file diff --git a/packages/file_picker/file_picker/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml similarity index 72% rename from packages/file_picker/file_picker/pubspec.yaml rename to packages/file_selector/file_selector/pubspec.yaml index 4679320c134a..ed3244de9aea 100644 --- a/packages/file_picker/file_picker/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -1,4 +1,4 @@ -name: file_picker +name: file_selector description: Flutter plugin for launching a URL on Android and iOS. Supports web, phone, SMS, and email schemes. homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher @@ -8,15 +8,15 @@ flutter: plugin: platforms: web: - default_package: file_picker_web + default_package: file_selector_web dependencies: flutter: sdk: flutter - file_picker_platform_interface: - path: ../file_picker_platform_interface - file_picker_web: - path: ../file_picker_web + file_selector_platform_interface: + path: ../file_selector_platform_interface + file_selector_web: + path: ../file_selector_web dev_dependencies: diff --git a/packages/file_picker/file_picker/test/file_picker_test.dart b/packages/file_selector/file_selector/test/file_picker_test.dart similarity index 100% rename from packages/file_picker/file_picker/test/file_picker_test.dart rename to packages/file_selector/file_selector/test/file_picker_test.dart diff --git a/packages/file_picker/file_picker_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md similarity index 100% rename from packages/file_picker/file_picker_platform_interface/CHANGELOG.md rename to packages/file_selector/file_selector_platform_interface/CHANGELOG.md diff --git a/packages/file_picker/file_picker_platform_interface/LICENSE b/packages/file_selector/file_selector_platform_interface/LICENSE similarity index 100% rename from packages/file_picker/file_picker_platform_interface/LICENSE rename to packages/file_selector/file_selector_platform_interface/LICENSE diff --git a/packages/file_picker/file_picker_platform_interface/README.md b/packages/file_selector/file_selector_platform_interface/README.md similarity index 100% rename from packages/file_picker/file_picker_platform_interface/README.md rename to packages/file_selector/file_selector_platform_interface/README.md diff --git a/packages/file_selector/file_selector_platform_interface/lib/file_selector_platform_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/file_selector_platform_interface.dart new file mode 100644 index 000000000000..69e3064150b5 --- /dev/null +++ b/packages/file_selector/file_selector_platform_interface/lib/file_selector_platform_interface.dart @@ -0,0 +1,2 @@ +export 'src/platform_interface/file_selector_interface.dart'; +export 'src/types/types.dart'; diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart similarity index 79% rename from packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index 5cdcf5d86fdb..bfaccb790c0f 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/method_channel/method_channel_file_picker.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -6,13 +6,13 @@ import 'dart:typed_data'; import 'package:flutter/services.dart'; -import '../platform_interface/file_picker_interface.dart'; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; +import '../platform_interface/file_selector_interface.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); -/// An implementation of [FilePickerPlatform] that uses method channels. -class MethodChannelFilePicker extends FilePickerPlatform { +/// An implementation of [FileSelectorPlatform] that uses method channels. +class MethodChannelFileSelector extends FileSelectorPlatform { /// Load a file from user's computer and return it as an XFile @override Future loadFile({List acceptedTypeGroups}) { diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart similarity index 68% rename from packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index 260def18f868..b2d526e7e8f8 100644 --- a/packages/file_picker/file_picker_platform_interface/lib/src/platform_interface/file_picker_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -5,10 +5,10 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import '../method_channel/method_channel_file_picker.dart'; +import '../method_channel/method_channel_file_selector.dart'; @@ -19,23 +19,23 @@ import '../method_channel/method_channel_file_picker.dart'; /// 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 -/// [FilePickerPlatform] methods. -abstract class FilePickerPlatform extends PlatformInterface { - /// Constructs a FilePickerPlatform. - FilePickerPlatform() : super(token: _token); +/// [FileSelectorPlatform] methods. +abstract class FileSelectorPlatform extends PlatformInterface { + /// Constructs a FileSelectorPlatform. + FileSelectorPlatform() : super(token: _token); static final Object _token = Object(); - static FilePickerPlatform _instance = MethodChannelFilePicker(); + static FileSelectorPlatform _instance = MethodChannelFileSelector(); - /// The default instance of [FilePickerPlatform] to use. + /// The default instance of [FileSelectorPlatform] to use. /// - /// Defaults to [MethodChannelFilePicker]. - static FilePickerPlatform get instance => _instance; + /// Defaults to [MethodChannelFileSelector]. + static FileSelectorPlatform get instance => _instance; /// Platform-specific plugins should set this with their own platform-specific - /// class that extends [FilePickerPlatform] when they register themselves. - static set instance(FilePickerPlatform instance) { + /// class that extends [FileSelectorPlatform] when they register themselves. + static set instance(FileSelectorPlatform instance) { PlatformInterface.verifyToken(instance, _token); _instance = instance; } diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/types.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/base.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/html.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/io.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/unsupported.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/x_file/x_file.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart diff --git a/packages/file_picker/file_picker_platform_interface/lib/src/types/x_type/x_type.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart similarity index 100% rename from packages/file_picker/file_picker_platform_interface/lib/src/types/x_type/x_type.dart rename to packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart diff --git a/packages/file_picker/file_picker_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml similarity index 94% rename from packages/file_picker/file_picker_platform_interface/pubspec.yaml rename to packages/file_selector/file_selector_platform_interface/pubspec.yaml index 9b8de97e38a6..71aa2f06d2b6 100644 --- a/packages/file_picker/file_picker_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -1,4 +1,4 @@ -name: file_picker_platform_interface +name: file_selector_platform_interface description: A common platform interface for the file_picker plugin. homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_platform_interface # NOTE: We strongly prefer non-breaking changes, even at the expense of a diff --git a/packages/file_picker/file_picker_platform_interface/test/file_picker_platform_interface_test.dart b/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart similarity index 91% rename from packages/file_picker/file_picker_platform_interface/test/file_picker_platform_interface_test.dart rename to packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart index fd5dd8deea2a..e454ac6ca951 100644 --- a/packages/file_picker/file_picker_platform_interface/test/file_picker_platform_interface_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart @@ -7,8 +7,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; -import 'package:file_picker_platform_interface/src/method_channel/method_channel_file_picker.dart'; +import 'package:file_picker_platform_interface/file_selector_platform_interface.dart'; +import 'package:file_picker_platform_interface/src/method_channel/method_channel_file_selector.dart'; void main() { group('$FilePickerPlatform', () { diff --git a/packages/file_picker/file_picker_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md similarity index 100% rename from packages/file_picker/file_picker_web/CHANGELOG.md rename to packages/file_selector/file_selector_web/CHANGELOG.md diff --git a/packages/file_picker/file_picker_web/LICENSE b/packages/file_selector/file_selector_web/LICENSE similarity index 100% rename from packages/file_picker/file_picker_web/LICENSE rename to packages/file_selector/file_selector_web/LICENSE diff --git a/packages/file_picker/file_picker_web/README.md b/packages/file_selector/file_selector_web/README.md similarity index 100% rename from packages/file_picker/file_picker_web/README.md rename to packages/file_selector/file_selector_web/README.md diff --git a/packages/file_picker/file_picker_web/lib/file_picker_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart similarity index 87% rename from packages/file_picker/file_picker_web/lib/file_picker_web.dart rename to packages/file_selector/file_selector_web/lib/file_selector_web.dart index a9f58bbf0f54..46d97059fc5b 100644 --- a/packages/file_picker/file_picker_web/lib/file_picker_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -2,32 +2,32 @@ import 'dart:async'; import 'dart:html'; import 'dart:typed_data'; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:meta/meta.dart'; -final String _kFilePickerInputsDomId = '__file_picker_web-file-input'; +final String _kFileSelectorInputsDomId = '__file_selector_web-file-input'; -/// The web implementation of [FilePickerPlatform]. +/// The web implementation of [FileSelectorPlatform]. /// -/// This class implements the `package:file_picker` functionality for the web. -class FilePickerPlugin extends FilePickerPlatform { +/// This class implements the `package:file_selector` functionality for the web. +class FileSelectorPlugin extends FileSelectorPlatform { Element _target; - final FilePickerPluginTestOverrides _overrides; + final FileSelectorPluginTestOverrides _overrides; bool get _hasTestOverrides => _overrides != null; /// Default constructor, initializes _target to a DOM element that we can use /// to host HTML elements. /// overrides parameter allows for testing to override functions - FilePickerPlugin({ - @visibleForTesting FilePickerPluginTestOverrides overrides, + FileSelectorPlugin({ + @visibleForTesting FileSelectorPluginTestOverrides overrides, }) : _overrides = overrides { - _target = _ensureInitialized(_kFilePickerInputsDomId); + _target = _ensureInitialized(_kFileSelectorInputsDomId); } - /// Registers this class as the default instance of [FilePickerPlatform]. + /// Registers this class as the default instance of [FileSelectorPlatform]. static void registerWith(Registrar registrar) { - FilePickerPlatform.instance = FilePickerPlugin(); + FileSelectorPlatform.instance = FileSelectorPlugin(); } /// Convert list of filter groups to a comma-separated string @@ -184,7 +184,7 @@ class FilePickerPlugin extends FilePickerPlatform { /// Overrides some functions to allow testing @visibleForTesting -class FilePickerPluginTestOverrides { +class FileSelectorPluginTestOverrides { /// For overriding the creation of the file input element. Element Function(String accepted) createFileInputElement; diff --git a/packages/file_picker/file_picker_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml similarity index 68% rename from packages/file_picker/file_picker_web/pubspec.yaml rename to packages/file_selector/file_selector_web/pubspec.yaml index 57cde59325a3..033bef65a459 100644 --- a/packages/file_picker/file_picker_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -1,6 +1,6 @@ -name: file_picker_web -description: Web platform implementation of file_picker -homepage: https://github.com/flutter/plugins/tree/master/packages/file_picker/file_picker +name: file_selector_web +description: Web platform implementation of file_selector +homepage: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector # 0.1.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 @@ -10,12 +10,12 @@ flutter: plugin: platforms: web: - pluginClass: FilePickerPlugin - fileName: file_picker_web.dart + pluginClass: FileSelectorPlugin + fileName: file_selector_web.dart dependencies: - file_picker_platform_interface: - path: ../file_picker_platform_interface + file_selector_platform_interface: + path: ../file_selector_platform_interface platform_detect: ^1.4.0 flutter: sdk: flutter diff --git a/packages/file_picker/file_picker_web/test/README.md b/packages/file_selector/file_selector_web/test/README.md similarity index 100% rename from packages/file_picker/file_picker_web/test/README.md rename to packages/file_selector/file_selector_web/test/README.md diff --git a/packages/file_picker/file_picker_web/test/lib/main.dart b/packages/file_selector/file_selector_web/test/lib/main.dart similarity index 100% rename from packages/file_picker/file_picker_web/test/lib/main.dart rename to packages/file_selector/file_selector_web/test/lib/main.dart diff --git a/packages/file_picker/file_picker_web/test/pubspec.yaml b/packages/file_selector/file_selector_web/test/pubspec.yaml similarity index 100% rename from packages/file_picker/file_picker_web/test/pubspec.yaml rename to packages/file_selector/file_selector_web/test/pubspec.yaml diff --git a/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart similarity index 98% rename from packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart rename to packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index 6442507f0f6e..d5bb4343cfe1 100644 --- a/packages/file_picker/file_picker_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -9,7 +9,7 @@ import 'dart:convert'; import 'dart:typed_data'; import 'dart:html'; -import 'package:file_picker_web/file_picker_web.dart'; +import 'package:file_picker_web/file_selector_web.dart'; import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; import 'package:platform_detect/test_utils.dart' as platform; diff --git a/packages/file_picker/file_picker_web/test/test_driver/web_e2e_test.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e_test.dart similarity index 100% rename from packages/file_picker/file_picker_web/test/test_driver/web_e2e_test.dart rename to packages/file_selector/file_selector_web/test/test_driver/web_e2e_test.dart diff --git a/packages/file_picker/file_picker_web/test/web/index.html b/packages/file_selector/file_selector_web/test/web/index.html similarity index 100% rename from packages/file_picker/file_picker_web/test/web/index.html rename to packages/file_selector/file_selector_web/test/web/index.html From 12bd4c080931ee597100d8a4df8e9b7c6b80baa1 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 18 Aug 2020 17:50:14 -0700 Subject: [PATCH 093/151] Update example app --- packages/file_selector/file_selector/example/lib/main.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 2ae04cfb2b4e..ea9b021db069 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -143,6 +143,3 @@ class _MyHomePageState extends State { ); } } - -class XPath { -} From 97512402a491663f11b974f149ff11502e333008 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 19 Aug 2020 17:41:21 -0700 Subject: [PATCH 094/151] Add lastModified with time zone adjustment --- .../lib/src/types/x_file/html.dart | 3 +++ .../lib/src/types/x_file/io.dart | 2 ++ .../file_selector_web/lib/file_selector_web.dart | 6 ++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index c1cc415c739f..799fab793444 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -19,6 +19,7 @@ class XFile extends XFileBase { final Uint8List _data; final int _length; final String name; + final DateTime lastModified; Element _target; /// Construct a XFile object from its ObjectUrl. @@ -34,6 +35,7 @@ class XFile extends XFileBase { this.name, int length, Uint8List bytes, + this.lastModified, }) : _data = bytes, _length = length, super(path) { @@ -47,6 +49,7 @@ class XFile extends XFileBase { this.type, this.name, int length, + this.lastModified, }) : _data = bytes, _length = length, super('') { diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart index 3538e6364804..dbb0706ab932 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart @@ -15,6 +15,8 @@ class XFile extends XFileBase { final XType type; + DateTime get lastModified => _file.lastModifiedSync(); + /// Construct a XFile object backed by a dart:io File. XFile(String path, { this.type }) : _file = File(path), diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 46d97059fc5b..6532981dff4c 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -71,13 +71,15 @@ class FileSelectorPlugin extends FileSelectorPlatform { List _getXFilesFromFiles (List files) { List xFiles = List(); + Duration timeZoneOffset = DateTime.now().timeZoneOffset; + for (File file in files) { String url = Url.createObjectUrl(file); String name = file.name; int length = file.size; - int modified = file.lastModified; + DateTime modified = file.lastModifiedDate.add(timeZoneOffset); - xFiles.add(XFile(url, name: name)); + xFiles.add(XFile(url, name: name, lastModified: modified)); } return xFiles; From 549bceedcd3f717373d13780456cdaea4a8de467 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Thu, 20 Aug 2020 13:39:45 -0700 Subject: [PATCH 095/151] Update API to include non-web supported features --- .../file_selector/lib/file_selector.dart | 15 ++++++++++++--- .../method_channel_file_selector.dart | 15 ++++++++++++--- .../file_selector_interface.dart | 15 ++++++++++++--- .../lib/file_selector_web.dart | 19 ++++++++++++++----- 4 files changed, 50 insertions(+), 14 deletions(-) diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index 26b5f5404329..de704e4b7e97 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -13,16 +13,25 @@ export 'package:file_selector_platform_interface/file_selector_platform_interfac /// NEW API /// Open file dialog for loading files and return a file path -Future loadFile({List acceptedTypeGroups}) { +Future loadFile({ + List acceptedTypeGroups, + String initialDirectory, +}) { return FileSelectorPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups); } /// Open file dialog for loading files and return a list of file paths -Future> loadFiles({List acceptedTypeGroups}) { +Future> loadFiles({ + List acceptedTypeGroups, + String initialDirectory, +}) { return FileSelectorPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups); } /// Saves File to user's file system -Future getSavePath() async { +Future getSavePath({ + String initialDirectory, + String suggestedName, +}) async { return FileSelectorPlatform.instance.getSavePath(); } \ No newline at end of file diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index bfaccb790c0f..6f8f8357d46a 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -15,7 +15,10 @@ const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); class MethodChannelFileSelector extends FileSelectorPlatform { /// Load a file from user's computer and return it as an XFile @override - Future loadFile({List acceptedTypeGroups}) { + Future loadFile({ + List acceptedTypeGroups, + String initialDirectory, + }) { return _channel.invokeMethod( 'loadFile', { @@ -26,7 +29,10 @@ class MethodChannelFileSelector extends FileSelectorPlatform { /// Load multiple files from user's computer and return it as an XFile @override - Future> loadFiles({List acceptedTypeGroups}) { + Future> loadFiles({ + List acceptedTypeGroups, + String initialDirectory, + }) { return _channel.invokeMethod>( 'loadFiles', { @@ -37,7 +43,10 @@ class MethodChannelFileSelector extends FileSelectorPlatform { /// Saves the file to user's Disk @override - Future getSavePath() async { + Future getSavePath({ + String initialDirectory, + String suggestedName, + }) async { return _channel.invokeMethod( 'saveFile', { diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index b2d526e7e8f8..94191fea7a0b 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -41,17 +41,26 @@ abstract class FileSelectorPlatform extends PlatformInterface { } /// Open file dialog for loading files and return a file path - Future loadFile({List acceptedTypeGroups}) { + Future loadFile({ + List acceptedTypeGroups, + String initialDirectory, + }) { throw UnimplementedError('loadFile() has not been implemented.'); } /// Open file dialog for loading files and return a list of file paths - Future> loadFiles({List acceptedTypeGroups}) { + Future> loadFiles({ + List acceptedTypeGroups, + String initialDirectory, + }) { throw UnimplementedError('loadFile() has not been implemented.'); } /// Open file dialog for saving files and return a file path at which to save - Future getSavePath() { + Future getSavePath({ + String initialDirectory, + String suggestedName, + }) { throw UnimplementedError('saveFile() has not been implemented.'); } } diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 6532981dff4c..8cd0a754cdae 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -160,9 +160,12 @@ class FileSelectorPlugin extends FileSelectorPlatform { return _getFilesWhenReady(element); } - /// Open file dialog for loading files and return a file path + /// Open file dialog for loading files and return a XFile @override - Future loadFile({List acceptedTypeGroups}) { + Future loadFile({ + List acceptedTypeGroups, + String initialDirectory, + }) { Completer _completer = Completer(); _loadFileHelper(false, acceptedTypeGroups).then((list) { _completer.complete(list.first); @@ -174,14 +177,20 @@ class FileSelectorPlugin extends FileSelectorPlatform { return _completer.future; } - /// Open file dialog for loading files and return a list of file paths + /// Open file dialog for loading files and return a XFile @override - Future> loadFiles({List acceptedTypeGroups}) { + Future> loadFiles({ + List acceptedTypeGroups, + String initialDirectory, + }) { return _loadFileHelper(true, acceptedTypeGroups); } @override - Future getSavePath() => Future.value(); + Future getSavePath({ + String initialDirectory, + String suggestedName, + }) => Future.value(); } /// Overrides some functions to allow testing From c7eea587cf74622b9d2946023a2433ba11b2f151 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Thu, 20 Aug 2020 14:09:55 -0700 Subject: [PATCH 096/151] Update Example App - Separate load and save --- .../file_selector/example/lib/main.dart | 138 +++++++++++++++--- 1 file changed, 121 insertions(+), 17 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index ea9b021db069..e5be679c0515 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -12,31 +12,32 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter Demo', + title: 'File Selector Demo', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(title: 'File Selector Demo Home Page'), + routes: { + '/save' : (context) => SaveTest(title: "Save Example"), + '/load' : (context) => LoadTest(title: "Load Example"), + }, ); } } -/// Home Page of the application -class MyHomePage extends StatefulWidget { - - /// Constructor for MyHomePage - MyHomePage({Key key, this.title}) : super(key: key); + +class SaveTest extends StatefulWidget { + SaveTest({Key key, this.title}) : super(key: key); /// Title of Home Page final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + _SaveTestState createState() => _SaveTestState(); } -/// State of Home Page -class _MyHomePageState extends State { +class _SaveTestState extends State { final TextEditingController _fileController = TextEditingController(); final TextEditingController _nameController = TextEditingController(); final TextEditingController _extensionController = TextEditingController(); @@ -63,6 +64,67 @@ class _MyHomePageState extends State { new_file.saveTo(''); } + @override + Widget build(BuildContext context) { + + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _nameController, + decoration: InputDecoration( + hintText: '(Optional) Suggest File Name', + ), + ), + ), + Container( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _fileController, + decoration: InputDecoration( + hintText: 'Enter File Contents', + ), + ), + ), + SizedBox(height: 10), + RaisedButton( + child: Text('Press to save file'), + onPressed: () => { _saveFile() }, + ), + ], + ), + ), + ); + } +} + +class LoadTest extends StatefulWidget { + LoadTest({Key key, this.title}) : super(key: key); + + /// Title of Home Page + final String title; + + + @override + _LoadTestState createState() => _LoadTestState(); +} + +class _LoadTestState extends State { + final TextEditingController _fileController = TextEditingController(); + final TextEditingController _nameController = TextEditingController(); + final TextEditingController _extensionController = TextEditingController(); + void _loadFile() async { XFile file; if (_extensionController.text.isNotEmpty) { @@ -88,7 +150,6 @@ class _MyHomePageState extends State { @override Widget build(BuildContext context) { - return Scaffold( appBar: AppBar( title: Text(widget.title), @@ -100,30 +161,27 @@ class _MyHomePageState extends State { Container( width: 300, child: TextField( + enabled: false, minLines: 1, maxLines: 12, controller: _nameController, decoration: InputDecoration( - hintText: '(Optional) Suggest File Name', + hintText: 'File Name Will Appear Here', ), ), ), Container( width: 300, child: TextField( + enabled: false, minLines: 1, maxLines: 12, controller: _fileController, decoration: InputDecoration( - hintText: 'Enter File Contents', + hintText: 'File Contents Will Appear Here', ), ), ), - SizedBox(height: 10), - RaisedButton( - child: Text('Press to save file'), - onPressed: () => { _saveFile() }, - ), Container( width: 300, child: TextField( @@ -143,3 +201,49 @@ class _MyHomePageState extends State { ); } } + + + +/// Home Page of the application +class MyHomePage extends StatefulWidget { + + /// Constructor for MyHomePage + MyHomePage({Key key, this.title}) : super(key: key); + + /// Title of Home Page + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +/// State of Home Page +class _MyHomePageState extends State { + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + + SizedBox(height: 10), + RaisedButton( + child: Text('Press to try saving a file'), + onPressed: () => Navigator.pushNamed(context, '/save'), + ), + RaisedButton( + child: Text('Press to try loading a file'), + onPressed: () => Navigator.pushNamed(context, '/load'), + ), + ], + ), + ), + ); + } + +} From 2cf24c1f5e63c9e9b3d2d10a82d1dd3975bfb777 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Thu, 20 Aug 2020 18:03:28 -0700 Subject: [PATCH 097/151] Load Images and Text Files in Example + Bug Fixes Bug Fix in XFile, querySelector requires valid CSS selector, which file names are not. Bug Fix in XType, extensions that start with '.' were not able to be translated to MIME. --- .../file_selector/example/lib/main.dart | 179 ++++++++++++------ .../lib/src/types/x_file/html.dart | 7 +- .../lib/src/types/x_type/x_type.dart | 7 +- 3 files changed, 132 insertions(+), 61 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index e5be679c0515..4d55d424dc21 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -26,7 +26,7 @@ class MyApp extends StatelessWidget { } } - +/// Page for showing an example of saving with file_selector class SaveTest extends StatefulWidget { SaveTest({Key key, this.title}) : super(key: key); @@ -49,6 +49,8 @@ class _SaveTestState extends State { } void _saveFile() async { + String path = await getSavePath(); + Uint8List data; data = Uint8List.fromList(_fileController.text.codeUnits); @@ -61,7 +63,7 @@ class _SaveTestState extends State { new_file = XFile.fromData(data, type: type, name: _nameController.text); } - new_file.saveTo(''); + new_file.saveTo(path); } @override @@ -99,7 +101,7 @@ class _SaveTestState extends State { ), SizedBox(height: 10), RaisedButton( - child: Text('Press to save file'), + child: Text('Press to save a text file'), onPressed: () => { _saveFile() }, ), ], @@ -109,7 +111,9 @@ class _SaveTestState extends State { } } +/// Screen that shows an example of loadFile(s) class LoadTest extends StatefulWidget { + /// Default constructor LoadTest({Key key, this.title}) : super(key: key); /// Title of Home Page @@ -121,31 +125,38 @@ class LoadTest extends StatefulWidget { } class _LoadTestState extends State { - final TextEditingController _fileController = TextEditingController(); - final TextEditingController _nameController = TextEditingController(); final TextEditingController _extensionController = TextEditingController(); - void _loadFile() async { - XFile file; - if (_extensionController.text.isNotEmpty) { - List typeGroups = List(); + void _onLoadImageFile() async { + XType jpg = XType(extension: '.jpg'); + XType png = XType(extension: '.png'); + XTypeGroup typeGroup = XTypeGroup(label: 'images', fileTypes: [ jpg, png ]); - List types = List(); - _extensionController.text.split(',').forEach((type) => types.add(XType(extension: type))); + XFile file = await loadFile(acceptedTypeGroups: [ typeGroup ]); - typeGroups.add(XTypeGroup(label: 'Example Files', fileTypes: types)); - file = await loadFile(acceptedTypeGroups: typeGroups); - } else { - file = await loadFile(); - } + await showDialog( + context: context, + builder: (context) { + return ImageDisplay(file: file); + } + ); - String text = await file.readAsString(); - _fileController.text = text; + } - if (file.name.isNotEmpty) { - _nameController.text = file.name; - } + void _onLoadTextFile() async { + XType txt = XType(extension: '.txt'); + XType json = XType(extension: '.json'); + XTypeGroup typeGroup = XTypeGroup(label: 'images', fileTypes: [ txt, json ]); + + XFile file = await loadFile(acceptedTypeGroups: [typeGroup]); + + await showDialog( + context: context, + builder: (context) { + return TextDisplay(file: file); + } + ); } @override @@ -158,42 +169,13 @@ class _LoadTestState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Container( - width: 300, - child: TextField( - enabled: false, - minLines: 1, - maxLines: 12, - controller: _nameController, - decoration: InputDecoration( - hintText: 'File Name Will Appear Here', - ), - ), - ), - Container( - width: 300, - child: TextField( - enabled: false, - minLines: 1, - maxLines: 12, - controller: _fileController, - decoration: InputDecoration( - hintText: 'File Contents Will Appear Here', - ), - ), - ), - Container( - width: 300, - child: TextField( - controller: _extensionController, - decoration: InputDecoration( - hintText: '(Optional) Accepted Load Extensions', - ), - ), + RaisedButton( + child: Text('Press to load an image file(png, jpg)'), + onPressed: () => _onLoadImageFile(), ), RaisedButton( - child: Text('Press to load a file'), - onPressed: () => { _loadFile() }, + child: Text('Press to load a text file (json, txt)'), + onPressed: () => _onLoadTextFile(), ), ], ), @@ -202,6 +184,92 @@ class _LoadTestState extends State { } } +/// Widget that displays a text file in a dialog +class TextDisplay extends StatefulWidget { + /// File to display + final XFile file; + + /// Default Constructor + TextDisplay({Key key, @required this.file}) : super(key: key); + + @override + _TextDisplayState createState() => _TextDisplayState(); +} + +class _TextDisplayState extends State { + String fileContents; + + @override + void initState() { + super.initState(); + _getFileContents(); + } + + void _getFileContents() async { + String contents = await widget.file.readAsString(); + setState(() => fileContents = contents); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(widget.file.name), + content: Scrollbar( + child: SingleChildScrollView( + child: Text( + fileContents ?? 'Loading file contents...\nThis may take a while if your file is large.', + ), + ), + ), + actions: [ + FlatButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} + +/// Widget that displays a text file in a dialog +class ImageDisplay extends StatefulWidget { + /// File to display + final XFile file; + + /// Default Constructor + ImageDisplay({Key key, @required this.file}) : super(key: key); + + @override + _ImageDisplayState createState() => _ImageDisplayState(); +} + +class _ImageDisplayState extends State { + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(widget.file.name), + content: Image.network(widget.file.path), + + actions: [ + FlatButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} + /// Home Page of the application @@ -230,7 +298,6 @@ class _MyHomePageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - SizedBox(height: 10), RaisedButton( child: Text('Press to try saving a file'), diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index 799fab793444..1eea5117a6ca 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -39,8 +39,6 @@ class XFile extends XFileBase { }) : _data = bytes, _length = length, super(path) { - // Create a DOM container where we can host the anchor. - _target = _ensureInitialized(this.name + '-x-file-dom-element'); } /// Construct an XFile from its data @@ -60,8 +58,6 @@ class XFile extends XFileBase { blob = Blob([bytes], type.mime); } this.path = Url.createObjectUrl(blob); - // Create a DOM container where we can host the anchor. - _target = _ensureInitialized(this.name + '-x-file-dom-element'); } @@ -97,6 +93,9 @@ class XFile extends XFileBase { /// Saves the data of this XFile at the location indicated by path. /// For the web implementation, the path variable is ignored. void saveTo(String path) async { + // Create a DOM container where we can host the anchor. + _target = _ensureInitialized('-x-file-dom-element'); + // Create an tag with the appropriate download attributes and click it final AnchorElement element = createAnchorElement(this.path, this.name); diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart index ace2f602d45b..77947f13fde2 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart @@ -41,7 +41,12 @@ class XType { String extension, String mime, }) : this._extension = extension, - this._mime = mime; + this._mime = mime + { + if (_extension != null && _extension[0] == '.') { + _extension = _extension.substring(1); + } + } /// Get the mime type from this XType String get mime { From ad4a5e7284c8c6372f56bc08d7ffdf1e5cea2f07 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 21 Aug 2020 14:58:37 -0700 Subject: [PATCH 098/151] API tests --- .../example/test/widget_test.dart | 15 ---- .../file_selector/lib/file_selector.dart | 6 +- .../file_selector/test/file_picker_test.dart | 8 --- .../test/file_selector_test.dart | 69 +++++++++++++++++++ 4 files changed, 72 insertions(+), 26 deletions(-) delete mode 100644 packages/file_selector/file_selector/test/file_picker_test.dart create mode 100644 packages/file_selector/file_selector/test/file_selector_test.dart diff --git a/packages/file_selector/file_selector/example/test/widget_test.dart b/packages/file_selector/file_selector/example/test/widget_test.dart index 747db1da35e8..c9a60e1f271f 100644 --- a/packages/file_selector/file_selector/example/test/widget_test.dart +++ b/packages/file_selector/file_selector/example/test/widget_test.dart @@ -11,20 +11,5 @@ 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); - }); } diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index de704e4b7e97..1da903db6ee1 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -17,7 +17,7 @@ Future loadFile({ List acceptedTypeGroups, String initialDirectory, }) { - return FileSelectorPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups); + return FileSelectorPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory); } /// Open file dialog for loading files and return a list of file paths @@ -25,7 +25,7 @@ Future> loadFiles({ List acceptedTypeGroups, String initialDirectory, }) { - return FileSelectorPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups); + return FileSelectorPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory); } /// Saves File to user's file system @@ -33,5 +33,5 @@ Future getSavePath({ String initialDirectory, String suggestedName, }) async { - return FileSelectorPlatform.instance.getSavePath(); + return FileSelectorPlatform.instance.getSavePath(initialDirectory: initialDirectory, suggestedName: suggestedName); } \ No newline at end of file diff --git a/packages/file_selector/file_selector/test/file_picker_test.dart b/packages/file_selector/file_selector/test/file_picker_test.dart deleted file mode 100644 index 6940fcfb4a6e..000000000000 --- a/packages/file_selector/file_selector/test/file_picker_test.dart +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -void main() { -} - diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart new file mode 100644 index 000000000000..85cf78cbe776 --- /dev/null +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -0,0 +1,69 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/material.dart'; +import 'package:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:file_selector/file_selector.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; + +void main() { + final MockFileSelector mock = MockFileSelector(); + FileSelectorPlatform.instance = mock; + + test('getSavePath', () async { + String expectedPath = '/example/path'; + + when( + mock.getSavePath( + initialDirectory: 'dir', + suggestedName: 'name', + ) + ).thenAnswer((_) => Future.value(expectedPath)); + + String result = await getSavePath(initialDirectory: 'dir', suggestedName: 'name'); + + expect(result, expectedPath); + }); + + test('loadFile', () async { + XFile file = XFile('path'); + + XTypeGroup typeGroup = XTypeGroup(label: 'group', fileTypes: [ XType(extension: '.json', mime: 'application/json') ]); + + when( + mock.loadFile( + acceptedTypeGroups: [typeGroup], + initialDirectory: 'dir', + ) + ).thenAnswer((_) => Future.value(file)); + + XFile result = await loadFile(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir'); + + expect(result, isNotNull); + }); + + test('loadFiles', () async { + XFile file = XFile('path'); + + XTypeGroup typeGroup = XTypeGroup(label: 'group', fileTypes: [ XType(extension: '.json', mime: 'application/json') ]); + + when( + mock.loadFiles( + acceptedTypeGroups: [typeGroup], + initialDirectory: 'dir', + ) + ).thenAnswer((_) => Future.value([file])); + + List result = await loadFiles(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir'); + + expect(result, isNotNull); + }); +} + +class MockFileSelector extends Mock + with MockPlatformInterfaceMixin + implements FileSelectorPlatform {} \ No newline at end of file From 155bb0840c4f1c897dda56ded2713a057766c608 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 21 Aug 2020 18:06:52 -0700 Subject: [PATCH 099/151] Test DOM container and input element creation Remove old tests --- .../lib/file_selector_web.dart | 35 +++--- .../file_selector_web/test/pubspec.yaml | 6 +- .../test/test_driver/web_e2e.dart | 115 +++++------------- 3 files changed, 49 insertions(+), 107 deletions(-) diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 8cd0a754cdae..260f3490b45b 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -47,7 +47,8 @@ class FileSelectorPlugin extends FileSelectorPlatform { /// Creates a file input element with only the accept attribute @visibleForTesting FileUploadInputElement createFileInputElement(String accepted, bool multiple) { - if (_hasTestOverrides) { + if (_hasTestOverrides && _overrides.createFileInputElement != null) { + print ("createFileInputElement overridden"); return _overrides.createFileInputElement(accepted); } @@ -88,30 +89,23 @@ class FileSelectorPlugin extends FileSelectorPlatform { /// Getter for retrieving files from an input element @visibleForTesting List getFilesFromInputElement(InputElement element) { - if(_hasTestOverrides) { + if(_hasTestOverrides && _overrides.getFilesFromInputElement != null) { + print ("getFilesFromInputElement overridden"); return _overrides.getFilesFromInputElement(element); } return element?.files ?? []; } - - Future _getFileWhenReady(InputElement element) { - final Completer _completer = Completer(); - - _getFilesWhenReady(element) - .then((list) { - _completer.complete(list[0]); - }) - .catchError((err) { - _completer.completeError(err); - }); - - return _completer.future; - } /// Listen for file input element to change and retrieve files when /// this happens. - Future> _getFilesWhenReady(InputElement element) { + @visibleForTesting + Future> getFilesWhenReady(InputElement element) { + if(_hasTestOverrides && _overrides.getFilesWhenReady != null) { + print ("getFilesWhenReady overridden"); + return _overrides.getFilesWhenReady(element); + } + final Completer> _completer = Completer(); // Listens for element change @@ -157,7 +151,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { _addElementToDomAndClick(element); - return _getFilesWhenReady(element); + return getFilesWhenReady(element); } /// Open file dialog for loading files and return a XFile @@ -201,4 +195,9 @@ class FileSelectorPluginTestOverrides { /// For overriding retrieving a file from the input element. List Function(InputElement input) getFilesFromInputElement; + + /// For overriding waiting for the files to be ready. Useful for testing so we do not hang here. + Future> Function(InputElement input) getFilesWhenReady; + + FileSelectorPluginTestOverrides({this.createFileInputElement, this.getFilesFromInputElement, this.getFilesWhenReady}); } \ No newline at end of file diff --git a/packages/file_selector/file_selector_web/test/pubspec.yaml b/packages/file_selector/file_selector_web/test/pubspec.yaml index c59457c1d9d6..12bbc2ac8720 100644 --- a/packages/file_selector/file_selector_web/test/pubspec.yaml +++ b/packages/file_selector/file_selector_web/test/pubspec.yaml @@ -9,10 +9,10 @@ dependencies: sdk: flutter dev_dependencies: - file_picker_web: + file_selector_web: path: ../ - file_picker_platform_interface: - path: ../../file_picker_platform_interface + file_selector_platform_interface: + path: ../../file_selector_platform_interface google_maps: ^3.4.4 flutter_driver: sdk: flutter diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index d5bb4343cfe1..b4e21fcc8295 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -9,112 +9,55 @@ import 'dart:convert'; import 'dart:typed_data'; import 'dart:html'; -import 'package:file_picker_web/file_selector_web.dart'; -import 'package:file_picker_platform_interface/file_picker_platform_interface.dart'; +import 'package:file_selector_web/file_selector_web.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:platform_detect/test_utils.dart' as platform; -final String expectedStringContents = 'Hello, world!'; -final expectedSize = expectedStringContents.length; -final Uint8List bytes = utf8.encode(expectedStringContents); -final File textFile = File([bytes], 'hello.txt'); - -final String expectedStringContents2 = 'This is the other test file'; -final expectedSize2 = expectedStringContents.length; -final Uint8List bytes2 = utf8.encode(expectedStringContents); -final File textFile2 = File([bytes], 'test2.txt'); +import 'dart:developer'; +final String domElementId = '__file_selector_web-file-input'; /// Test Markers void main() { E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; - group('loadFile: ', () { - test('Select a single file to load', () async { - final mockInput = FileUploadInputElement(); - - // Note that we override the retrieval of files from the input element. - // We opt to do this because dart cannot edit the "files" attribute of - // tag. When performing mockInput.files = [ textFile ] we receive: - // "Failed to set the 'files' property on 'HTMLInputElement': The provided - // value is not of type 'FileList'." - // - // More on this (javascript side): https://stackoverflow.com/questions/52078853/is-it-possible-to-update-filelist - // The dart implementation will depend on the javascript it creates. - final plugin = FilePickerPlugin( - overrides: FilePickerPluginTestOverrides() - ..createFileInputElement = ((_) => mockInput) - ..getFilesFromInputElement = ((_) => [textFile]) - ); - - // Call load file - final files = plugin.loadFile(); - - // Mock selection of a file - mockInput.dispatchEvent(Event('change')); + FileSelectorPlugin plugin; - // Expect the file to complete - expect(files, completes); + testWidgets('Create a DOM container', (WidgetTester tester) { + plugin = FileSelectorPlugin(); - // Expect that we can read from the file - final loadedFiles = await files; - final loadedFile = loadedFiles.first; - expect(loadedFile.readAsBytes(), completion(isNotEmpty)); - expect(loadedFile.length(), completion(equals(expectedSize))); - expect(loadedFile.name, textFile.name); - }); - - test('Select multiple files to load', () async { - final mockInput = FileUploadInputElement(); + final result = querySelector('#${domElementId}'); + expect(result, isNotNull); + }); - final plugin = FilePickerPlugin( - overrides: FilePickerPluginTestOverrides() - ..createFileInputElement = ((_) => mockInput) - ..getFilesFromInputElement = ((_) => [textFile, textFile2]) + group('loadFile(..)', () { + testWidgets('creates correct input element', (WidgetTester tester) async { + final overrides = FileSelectorPluginTestOverrides( + getFilesWhenReady: (_) => Future.value([ XFile('path') ]), ); - // Call load file - final files = plugin.loadFile(); - - // Mock selection of a file - mockInput.dispatchEvent(Event('change')); + plugin = FileSelectorPlugin( + overrides: overrides, + ); - // Expect the file to complete - expect(files, completes); + final container = querySelector('#${domElementId}'); - // Expect that we can read from the files - final loadedFiles = await files; - final file1 = loadedFiles[0]; - expect(file1.readAsBytes(), completion(isNotEmpty)); - expect(file1.length(), completion(equals(expectedSize))); - expect(file1.name, textFile.name); + final typeGroup = XTypeGroup(label: 'test', + fileTypes: [ + XType(extension: 'json', mime: 'application/json'), + XType(extension: 'txt', mime: 'text/plain'), + ]); - final file2 = loadedFiles[1]; - expect(file2.readAsBytes(), completion(isNotEmpty)); - expect(file2.length(), completion(equals(expectedSize2))); - expect(file2.name, textFile2.name); - }); - }); + final file = await plugin.loadFile(acceptedTypeGroups: [ typeGroup ]); - group('saveFile: ', () { - test('Create a blob', () { - Uint8List data = Uint8List.fromList(expectedStringContents.codeUnits); - - FilePickerPlugin plugin = FilePickerPlugin(); - final blob = plugin.createBlob(data, 'text/plain'); - - expect(blob.type, 'text/plain'); - expect(blob.size, expectedSize); - }); + expect(file, isNotNull); - test('Create an anchor', () { - FilePickerPlugin plugin = FilePickerPlugin(); - final String href = 'https://google.com'; - final String name = 'file_name.txt'; - final anchor = plugin.createAnchorElement(href, name); + final result = container?.children?.firstWhere((element) => element.tagName == 'INPUT', orElse: () => null); - expect(anchor.download, name); - expect(anchor.href, href); + expect(result, isNotNull); + expect(result.getAttribute('type'), 'file'); + expect(result.getAttribute('accept'), '.json,.txt'); }); }); } From 19d1e47a82296723a30798077c7a172a28954e9a Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Sun, 23 Aug 2020 17:55:06 -0700 Subject: [PATCH 100/151] loadFile, loadFiles, and getSavePath tests --- .../lib/file_selector_web.dart | 13 +- .../test/test_driver/web_e2e.dart | 166 +++++++++++++++++- 2 files changed, 164 insertions(+), 15 deletions(-) diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 260f3490b45b..8b60dd57ee10 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -48,8 +48,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { @visibleForTesting FileUploadInputElement createFileInputElement(String accepted, bool multiple) { if (_hasTestOverrides && _overrides.createFileInputElement != null) { - print ("createFileInputElement overridden"); - return _overrides.createFileInputElement(accepted); + return _overrides.createFileInputElement(accepted, multiple); } final FileUploadInputElement element = FileUploadInputElement(); @@ -71,7 +70,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { List _getXFilesFromFiles (List files) { List xFiles = List(); - + Duration timeZoneOffset = DateTime.now().timeZoneOffset; for (File file in files) { @@ -90,7 +89,6 @@ class FileSelectorPlugin extends FileSelectorPlatform { @visibleForTesting List getFilesFromInputElement(InputElement element) { if(_hasTestOverrides && _overrides.getFilesFromInputElement != null) { - print ("getFilesFromInputElement overridden"); return _overrides.getFilesFromInputElement(element); } @@ -102,7 +100,6 @@ class FileSelectorPlugin extends FileSelectorPlatform { @visibleForTesting Future> getFilesWhenReady(InputElement element) { if(_hasTestOverrides && _overrides.getFilesWhenReady != null) { - print ("getFilesWhenReady overridden"); return _overrides.getFilesWhenReady(element); } @@ -114,9 +111,9 @@ class FileSelectorPlugin extends FileSelectorPlatform { final List files = getFilesFromInputElement(element); // Create XFile from dart:html Files - final returnPaths = _getXFilesFromFiles(files); + final xFiles = _getXFilesFromFiles(files); - _completer.complete(returnPaths); + _completer.complete(xFiles); }); element.onError.first.then((event) { @@ -191,7 +188,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { @visibleForTesting class FileSelectorPluginTestOverrides { /// For overriding the creation of the file input element. - Element Function(String accepted) createFileInputElement; + Element Function(String accepted, bool multiple) createFileInputElement; /// For overriding retrieving a file from the input element. List Function(InputElement input) getFilesFromInputElement; diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index b4e21fcc8295..c40b124ba551 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -18,6 +18,20 @@ import 'dart:developer'; final String domElementId = '__file_selector_web-file-input'; +final textGroup = XTypeGroup(label: 'test', + fileTypes: [ + XType(extension: 'json', mime: 'application/json'), + XType(extension: 'txt', mime: 'text/plain'), + ]); + +final String expectedStringContents = 'Hello, world!'; +final Uint8List bytes = utf8.encode(expectedStringContents); +final File textFile = File([bytes], 'hello.txt'); + +final String expectedStringContents2 = 'This is the other test file'; +final Uint8List bytes2 = utf8.encode(expectedStringContents2); +final File textFile2 = File([bytes2], 'test2.txt'); + /// Test Markers void main() { E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; @@ -43,13 +57,8 @@ void main() { final container = querySelector('#${domElementId}'); - final typeGroup = XTypeGroup(label: 'test', - fileTypes: [ - XType(extension: 'json', mime: 'application/json'), - XType(extension: 'txt', mime: 'text/plain'), - ]); - - final file = await plugin.loadFile(acceptedTypeGroups: [ typeGroup ]); + print("Expect message: 'File chooser dialog can only be shown with a user activation'"); + final file = await plugin.loadFile(acceptedTypeGroups: [ textGroup ]); expect(file, isNotNull); @@ -59,5 +68,148 @@ void main() { expect(result.getAttribute('type'), 'file'); expect(result.getAttribute('accept'), '.json,.txt'); }); + + testWidgets('input element is clicked', (WidgetTester tester) async { + final mockInput = FileUploadInputElement(); + + final overrides = FileSelectorPluginTestOverrides( + getFilesWhenReady: (_) => Future.value([ XFile('path') ]), + createFileInputElement: (_, __) => mockInput, + ); + + + + plugin = FileSelectorPlugin( + overrides: overrides, + ); + + bool clicked = false; + mockInput.onClick.listen((event) => clicked = true); + + print("Expect message: 'File chooser dialog can only be shown with a user activation'"); + final file = await plugin.loadFile(acceptedTypeGroups: [ textGroup ]); + + expect(clicked, true); + }); + + testWidgets('get XFile from input element', (WidgetTester tester) async { + final mockInput = FileUploadInputElement(); + + final overrides = FileSelectorPluginTestOverrides( + getFilesFromInputElement: (_) => [textFile], + createFileInputElement: (_, __) => mockInput, + ); + + plugin = FileSelectorPlugin( + overrides: overrides, + ); + + // Call load file + final file = plugin.loadFile(); + + // Mock selection of a file + mockInput.dispatchEvent(Event('change')); + + // Expect the file to complete + expect(file, completes); + + // Expect that we can read from the file + final loadedFile = await file; + final contents = await loadedFile.readAsString(); + expect(contents, expectedStringContents); + expect(loadedFile.name, textFile.name); + }); }); + + + group('loadFiles(..)', () { + testWidgets('creates correct input element', (WidgetTester tester) async { + final overrides = FileSelectorPluginTestOverrides( + getFilesWhenReady: (_) => Future.value([ XFile('path'), XFile('path2') ]), + ); + + plugin = FileSelectorPlugin( + overrides: overrides, + ); + + final container = querySelector('#${domElementId}'); + + print("Expect message: 'File chooser dialog can only be shown with a user activation'"); + final files = await plugin.loadFiles(acceptedTypeGroups: [ textGroup ]); + + expect(files, isNotNull); + + final FileUploadInputElement result = container?.children?.firstWhere((element) => element.tagName == 'INPUT', orElse: () => null); + + expect(result, isNotNull); + expect(result.getAttribute('type'), 'file'); + expect(result.getAttribute('accept'), '.json,.txt'); + expect(result.multiple, true); + }); + + testWidgets('input element is clicked', (WidgetTester tester) async { + final mockInput = FileUploadInputElement(); + + final overrides = FileSelectorPluginTestOverrides( + getFilesWhenReady: (_) => Future.value([ XFile('path') ]), + createFileInputElement: (_, __) => mockInput, + ); + + + + plugin = FileSelectorPlugin( + overrides: overrides, + ); + + bool clicked = false; + mockInput.onClick.listen((event) => clicked = true); + + print("Expect message: 'File chooser dialog can only be shown with a user activation'"); + final file = await plugin.loadFiles(acceptedTypeGroups: [ textGroup ]); + + expect(clicked, true); + }); + + testWidgets('get XFiles from input element', (WidgetTester tester) async { + final mockInput = FileUploadInputElement(); + + final overrides = FileSelectorPluginTestOverrides( + getFilesFromInputElement: (_) => [textFile, textFile2], + createFileInputElement: (_, __) => mockInput, + ); + + plugin = FileSelectorPlugin( + overrides: overrides, + ); + + // Call load file + final files = plugin.loadFiles(); + + // Mock selection of files + mockInput.dispatchEvent(Event('change')); + + // Expect the file to complete + expect(files, completes); + + // Expect that we can read from the file + final loadedFiles = await files; + final loadedFile1 = loadedFiles[0]; + final loadedFile2 = loadedFiles[1]; + + final contents = await loadedFile1.readAsString(); + expect(contents, expectedStringContents); + expect(loadedFile1.name, textFile.name); + + final contents2 = await loadedFile2.readAsString(); + expect(contents2, expectedStringContents2); + expect(loadedFile2.name, textFile2.name); + }); + }); + + testWidgets('getSavePath completes', (WidgetTester tester) async { + plugin = FileSelectorPlugin(); + final path = plugin.getSavePath(); + expect(path, completes); + }); + } From bbe56f13f7a1da4b0d54572ab9de1fa0706ea865 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 24 Aug 2020 11:19:15 -0700 Subject: [PATCH 101/151] XFile saveTo(..) tests --- .../lib/src/types/x_file/html.dart | 32 ++++++++-- .../lib/file_selector_web.dart | 2 +- .../test/test_driver/web_e2e.dart | 59 ++++++++++++++++--- 3 files changed, 78 insertions(+), 15 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index 1eea5117a6ca..321457193128 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -22,6 +22,10 @@ class XFile extends XFileBase { final DateTime lastModified; Element _target; + final XFileTestOverrides _overrides; + + bool get _hasTestOverrides => _overrides != null; + /// Construct a XFile object from its ObjectUrl. /// /// Optionally, this can be initialized with `bytes` and `length` @@ -36,10 +40,11 @@ class XFile extends XFileBase { int length, Uint8List bytes, this.lastModified, + @visibleForTesting XFileTestOverrides overrides, }) : _data = bytes, _length = length, - super(path) { - } + _overrides = overrides, + super(path); /// Construct an XFile from its data XFile.fromData( @@ -48,11 +53,13 @@ class XFile extends XFileBase { this.name, int length, this.lastModified, + @visibleForTesting XFileTestOverrides overrides, }) : _data = bytes, _length = length, + _overrides = overrides, super('') { Blob blob; - if (type.mime == null) { + if (type == null) { blob = Blob([bytes]); } else { blob = Blob([bytes], type.mime); @@ -94,7 +101,7 @@ class XFile extends XFileBase { /// For the web implementation, the path variable is ignored. void saveTo(String path) async { // Create a DOM container where we can host the anchor. - _target = _ensureInitialized('-x-file-dom-element'); + _target = _ensureInitialized('__x_file_dom_element'); // Create an tag with the appropriate download attributes and click it final AnchorElement element = createAnchorElement(this.path, this.name); @@ -105,6 +112,10 @@ class XFile extends XFileBase { /// Create anchor element with download attribute @visibleForTesting AnchorElement createAnchorElement(String href, String suggestedName) { + if (_hasTestOverrides && _overrides.createAnchorElement != null) { + return _overrides.createAnchorElement(href, suggestedName); + } + final element = AnchorElement(href: href); element.download = suggestedName; return element; @@ -123,11 +134,22 @@ class XFile extends XFileBase { var target = querySelector('#${id}'); if (target == null) { final Element targetElement = - Element.tag('flt-x-file-input')..id = id; + Element.tag('flt-x-file')..id = id; querySelector('body').children.add(targetElement); target = targetElement; } return target; } +} + + +/// Overrides some functions to allow testing +@visibleForTesting +class XFileTestOverrides { + /// For overriding the creation of the file input element. + Element Function(String href, String suggestedName) createAnchorElement; + + /// Default constructor for overrides + XFileTestOverrides({this.createAnchorElement}); } \ No newline at end of file diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 8b60dd57ee10..d98a8921df75 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -6,7 +6,7 @@ import 'package:file_selector_platform_interface/file_selector_platform_interfac import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:meta/meta.dart'; -final String _kFileSelectorInputsDomId = '__file_selector_web-file-input'; +final String _kFileSelectorInputsDomId = '__file_selector_web_file_input'; /// The web implementation of [FileSelectorPlatform]. /// diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index c40b124ba551..89bb1d0904f1 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -16,7 +16,8 @@ import 'package:platform_detect/test_utils.dart' as platform; import 'dart:developer'; -final String domElementId = '__file_selector_web-file-input'; +final String domElementId = '__file_selector_web_file_input'; +final String xFileDomElementId = '__x_file_dom_element'; final textGroup = XTypeGroup(label: 'test', fileTypes: [ @@ -36,6 +37,8 @@ final File textFile2 = File([bytes2], 'test2.txt'); void main() { E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; + print("Note that the following message can be ignored in the test output:\n'File chooser dialog can only be shown with a user activation'"); + FileSelectorPlugin plugin; testWidgets('Create a DOM container', (WidgetTester tester) { @@ -57,7 +60,6 @@ void main() { final container = querySelector('#${domElementId}'); - print("Expect message: 'File chooser dialog can only be shown with a user activation'"); final file = await plugin.loadFile(acceptedTypeGroups: [ textGroup ]); expect(file, isNotNull); @@ -76,9 +78,7 @@ void main() { getFilesWhenReady: (_) => Future.value([ XFile('path') ]), createFileInputElement: (_, __) => mockInput, ); - - - + plugin = FileSelectorPlugin( overrides: overrides, ); @@ -86,7 +86,6 @@ void main() { bool clicked = false; mockInput.onClick.listen((event) => clicked = true); - print("Expect message: 'File chooser dialog can only be shown with a user activation'"); final file = await plugin.loadFile(acceptedTypeGroups: [ textGroup ]); expect(clicked, true); @@ -134,7 +133,6 @@ void main() { final container = querySelector('#${domElementId}'); - print("Expect message: 'File chooser dialog can only be shown with a user activation'"); final files = await plugin.loadFiles(acceptedTypeGroups: [ textGroup ]); expect(files, isNotNull); @@ -164,7 +162,6 @@ void main() { bool clicked = false; mockInput.onClick.listen((event) => clicked = true); - print("Expect message: 'File chooser dialog can only be shown with a user activation'"); final file = await plugin.loadFiles(acceptedTypeGroups: [ textGroup ]); expect(clicked, true); @@ -195,7 +192,7 @@ void main() { final loadedFiles = await files; final loadedFile1 = loadedFiles[0]; final loadedFile2 = loadedFiles[1]; - + final contents = await loadedFile1.readAsString(); expect(contents, expectedStringContents); expect(loadedFile1.name, textFile.name); @@ -212,4 +209,48 @@ void main() { expect(path, completes); }); + group('XFile saveTo(..)', () { + testWidgets('creates a DOM container', (WidgetTester tester) async { + XFile file = XFile.fromData(bytes); + + await file.saveTo(''); + + final container = querySelector('#${xFileDomElementId}'); + + expect(container, isNotNull); + }); + + testWidgets('create anchor element', (WidgetTester tester) async { + XFile file = XFile.fromData(bytes, name: textFile.name); + + await file.saveTo('path'); + + final container = querySelector('#${xFileDomElementId}'); + final AnchorElement element = container?.children?.firstWhere((element) => element.tagName == 'A', orElse: () => null); + + expect(element, isNotNull); + expect(element.href, file.path); + expect(element.download, file.name); + }); + + testWidgets('anchor element is clicked', (WidgetTester tester) async { + final mockAnchor = AnchorElement(); + + XFileTestOverrides overrides = XFileTestOverrides( + createAnchorElement: (_,__) => mockAnchor, + ); + + XFile file = XFile.fromData(bytes, name: textFile.name, overrides: overrides); + + bool clicked = false; + mockAnchor.onClick.listen((event) => clicked = true); + + await file.saveTo('path'); + + expect(clicked, true); + }); + }); + + print("Note that the following message can be ignored in the test output:\n'File chooser dialog can only be shown with a user activation'"); + } From 2440e1c978d9a2ef4157d6ffb914241e22188964 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 24 Aug 2020 14:23:14 -0700 Subject: [PATCH 102/151] update Method Channel --- .../lib/src/method_channel/method_channel_file_selector.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index 6f8f8357d46a..91ab40c9f840 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -23,6 +23,7 @@ class MethodChannelFileSelector extends FileSelectorPlatform { 'loadFile', { 'acceptedTypes': acceptedTypeGroups, + 'initialDirectory': initialDirectory, }, ); } @@ -37,6 +38,7 @@ class MethodChannelFileSelector extends FileSelectorPlatform { 'loadFiles', { 'acceptedTypes': acceptedTypeGroups, + 'initialDirectory': initialDirectory, }, ); } @@ -50,7 +52,8 @@ class MethodChannelFileSelector extends FileSelectorPlatform { return _channel.invokeMethod( 'saveFile', { - + 'initialDirectory': initialDirectory, + 'suggestedName': suggestedName, }, ); } From 7425f728ff9f2a1e62a37e2686adf7ef97c8bb16 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 24 Aug 2020 18:33:20 -0700 Subject: [PATCH 103/151] Enforce txt file in anchor (Example app) --- packages/file_selector/file_selector/example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 4d55d424dc21..80876af3960a 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -60,7 +60,7 @@ class _SaveTestState extends State { if (_nameController.text == '') { new_file = XFile.fromData(data, type: type); } else { - new_file = XFile.fromData(data, type: type, name: _nameController.text); + new_file = XFile.fromData(data, type: type, name: _nameController.text + '.txt'); } new_file.saveTo(path); From 884eb1ded146d0a1de543a5ffb45e8454f50f119 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 25 Aug 2020 10:24:31 -0700 Subject: [PATCH 104/151] Accept extensions in web saving --- .../file_selector/example/lib/main.dart | 4 ++-- .../lib/src/types/x_file/html.dart | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 80876af3960a..f658e256ec34 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -55,12 +55,12 @@ class _SaveTestState extends State { data = Uint8List.fromList(_fileController.text.codeUnits); XFile new_file; - XType type = XType(extension: '.txt'); + XType type = XType(extension: '.txt', mime: 'plain/text'); if (_nameController.text == '') { new_file = XFile.fromData(data, type: type); } else { - new_file = XFile.fromData(data, type: type, name: _nameController.text + '.txt'); + new_file = XFile.fromData(data, type: type, name: _nameController.text); } new_file.saveTo(path); diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index 321457193128..f06333a185dc 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -59,7 +59,7 @@ class XFile extends XFileBase { _overrides = overrides, super('') { Blob blob; - if (type == null) { + if (type.mime == null) { blob = Blob([bytes]); } else { blob = Blob([bytes], type.mime); @@ -117,7 +117,17 @@ class XFile extends XFileBase { } final element = AnchorElement(href: href); - element.download = suggestedName; + + if (suggestedName == null) { + element.download = 'download'; + } else { + element.download = suggestedName; + } + + if (type.extension != null) { + element.download += '.' + type.extension; + } + return element; } From 70752653e97974fb23a41764a79201ca327ff4db Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 25 Aug 2020 11:59:49 -0700 Subject: [PATCH 105/151] Include extensions in loadFile --- .../lib/src/types/x_type/x_type.dart | 7 +------ .../file_selector_web/lib/file_selector_web.dart | 11 ++++++++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart index 77947f13fde2..ace2f602d45b 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart @@ -41,12 +41,7 @@ class XType { String extension, String mime, }) : this._extension = extension, - this._mime = mime - { - if (_extension != null && _extension[0] == '.') { - _extension = _extension.substring(1); - } - } + this._mime = mime; /// Get the mime type from this XType String get mime { diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index d98a8921df75..59098d2e853e 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -35,10 +35,15 @@ class FileSelectorPlugin extends FileSelectorPlatform { List allExtensions = List(); for (XTypeGroup group in acceptedTypes ?? []) { for (XType type in group.fileTypes ?? []) { - if (type.extension == null) { - continue; + if (type.extension != null) { + String extension = type.extension; + if (extension[0] != '.') { + extension = '.' + extension; + } + allExtensions.add(extension); + } else if (type.mime != null) { + allExtensions.add(type.mime); } - allExtensions.add('.' + type.extension); } } return allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; From 05fafbd2d98b7121b35ed68bc24d35a69662b8fe Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 26 Aug 2020 16:47:52 -0700 Subject: [PATCH 106/151] Update XTypeGroup to new API --- .../file_selector/example/lib/main.dart | 14 ++--- .../file_selector/lib/file_selector.dart | 2 +- .../lib/src/types/types.dart | 2 +- .../lib/src/types/x_file/base.dart | 12 +++- .../lib/src/types/x_file/html.dart | 24 +++----- .../lib/src/types/x_file/io.dart | 52 ++++++++++------ .../lib/src/types/x_type/x_type.dart | 61 ------------------- .../src/types/x_type_group/x_type_group.dart | 53 ++++++++++++++++ .../lib/file_selector_web.dart | 33 ++++++---- .../test/test_driver/web_e2e.dart | 20 +++--- 10 files changed, 150 insertions(+), 123 deletions(-) delete mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart create mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index f658e256ec34..0c79faa72fc9 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -55,12 +55,11 @@ class _SaveTestState extends State { data = Uint8List.fromList(_fileController.text.codeUnits); XFile new_file; - XType type = XType(extension: '.txt', mime: 'plain/text'); if (_nameController.text == '') { - new_file = XFile.fromData(data, type: type); + new_file = XFile.fromData(data, mimeType: 'text/plain'); } else { - new_file = XFile.fromData(data, type: type, name: _nameController.text); + new_file = XFile.fromData(data, mimeType: 'text/plain', name: _nameController.text); } new_file.saveTo(path); @@ -128,9 +127,8 @@ class _LoadTestState extends State { final TextEditingController _extensionController = TextEditingController(); void _onLoadImageFile() async { - XType jpg = XType(extension: '.jpg'); - XType png = XType(extension: '.png'); - XTypeGroup typeGroup = XTypeGroup(label: 'images', fileTypes: [ jpg, png ]); + + XTypeGroup typeGroup = XTypeGroup(label: 'images', extensions: [ 'jpg', 'png' ]); XFile file = await loadFile(acceptedTypeGroups: [ typeGroup ]); @@ -145,9 +143,7 @@ class _LoadTestState extends State { } void _onLoadTextFile() async { - XType txt = XType(extension: '.txt'); - XType json = XType(extension: '.json'); - XTypeGroup typeGroup = XTypeGroup(label: 'images', fileTypes: [ txt, json ]); + XTypeGroup typeGroup = XTypeGroup(label: 'images', extensions: [ 'txt', 'json' ]); XFile file = await loadFile(acceptedTypeGroups: [typeGroup]); diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index 1da903db6ee1..d533373e2855 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -8,7 +8,7 @@ import 'dart:typed_data'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; export 'package:file_selector_platform_interface/file_selector_platform_interface.dart' - show XFile, XTypeGroup, XType; + show XFile, XTypeGroup; /// NEW API diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart index cae9d41efedf..8848c6751ba3 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart @@ -1,3 +1,3 @@ export 'x_file/x_file.dart'; -export 'x_type/x_type.dart'; +export 'x_type_group/x_type_group.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart index 1b5c81f9bde0..be9db430e924 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart @@ -1,6 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; -import '../x_type/x_type.dart'; +import '../x_type_group/x_type_group.dart'; /// The interface for a XFile. /// @@ -39,6 +39,11 @@ abstract class XFileBase { throw UnimplementedError('.name has not been implemented.'); } + /// For web, it may be necessary for a file to know its MIME type. + String get mimeType { + throw UnimplementedError('.mimeType has not been implemented.'); + } + /// Get the length of the file. Returns a `Future` that completes with the length in bytes. Future length() { throw UnimplementedError('.length() has not been implemented.'); @@ -70,4 +75,9 @@ abstract class XFileBase { Stream openRead([int start, int end]) { throw UnimplementedError('openRead() has not been implemented.'); } + + /// Get the last-modified time for the XFile + Future lastModified() { + throw UnimplementedError('openRead() has not been implemented.'); + } } \ No newline at end of file diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index f06333a185dc..7a3cde9ca7fb 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -7,19 +7,17 @@ import 'dart:html'; import './base.dart'; -import '../x_type/x_type.dart'; - /// A XFile that works on web. /// /// It wraps the bytes of a selected file. class XFile extends XFileBase { String path; - final XType type; + final String mimeType; final Uint8List _data; final int _length; final String name; - final DateTime lastModified; + final DateTime _lastModified; Element _target; final XFileTestOverrides _overrides; @@ -35,34 +33,36 @@ class XFile extends XFileBase { /// access to it while we create the ObjectUrl. XFile( this.path, { - this.type, + this.mimeType, this.name, int length, Uint8List bytes, - this.lastModified, + DateTime lastModified, @visibleForTesting XFileTestOverrides overrides, }) : _data = bytes, _length = length, _overrides = overrides, + _lastModified = lastModified, super(path); /// Construct an XFile from its data XFile.fromData( Uint8List bytes, { - this.type, + this.mimeType, this.name, int length, - this.lastModified, + DateTime lastModified, @visibleForTesting XFileTestOverrides overrides, }) : _data = bytes, _length = length, _overrides = overrides, + _lastModified = lastModified, super('') { Blob blob; - if (type.mime == null) { + if (mimeType == null) { blob = Blob([bytes]); } else { - blob = Blob([bytes], type.mime); + blob = Blob([bytes], mimeType); } this.path = Url.createObjectUrl(blob); } @@ -124,10 +124,6 @@ class XFile extends XFileBase { element.download = suggestedName; } - if (type.extension != null) { - element.download += '.' + type.extension; - } - return element; } diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart index dbb0706ab932..536b4456eca2 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart @@ -9,39 +9,55 @@ import '../types.dart'; /// A XFile backed by a dart:io File. class XFile extends XFileBase { final File _file; + final String mimeType; + final DateTime _lastModified; int _length; - final Uint8List _data; - - final XType type; - - DateTime get lastModified => _file.lastModifiedSync(); + final Uint8List _bytes; /// Construct a XFile object backed by a dart:io File. - XFile(String path, { this.type }) + XFile( + String path, { + this.mimeType, + String name, + int length, + Uint8List bytes, + DateTime lastModified, + }) : _file = File(path), - _data = null, + _bytes = null, + _lastModified = lastModified, super(path); /// Construct an XFile from its data - XFile.fromData(Uint8List data, { - this.type, + XFile.fromData(Uint8List bytes, { + this.mimeType, String path, String name, int length, - }): _data = data, + DateTime lastModified, + }): _bytes = bytes, _file = File(path), _length = length, + _lastModified = lastModified, super(path) { if (length == null) { - _length = data.length; + _length = bytes.length; + } + } + + @override + Future lastModified() { + if (_lastModified != null) { + return Future.value(_lastModified); } + return _file.lastModified(); } @override void saveTo(String path) async { File fileToSave = File(path); - await fileToSave.writeAsBytes(_data); + await fileToSave.writeAsBytes(_bytes); await fileToSave.create(); } @@ -65,24 +81,24 @@ class XFile extends XFileBase { @override Future readAsString({Encoding encoding = utf8}) { - if (_data != null) { - return Future.value(String.fromCharCodes(_data)); + if (_bytes != null) { + return Future.value(String.fromCharCodes(_bytes)); } return _file.readAsString(encoding: encoding); } @override Future readAsBytes() { - if (_data != null) { - return Future.value(_data); + if (_bytes != null) { + return Future.value(_bytes); } return _file.readAsBytes(); } @override Stream openRead([int start, int end]) async* { - if (_data != null) { - final bytes = _data; + if (_bytes != null) { + final bytes = _bytes; yield bytes.sublist(start ?? 0, end ?? bytes.length); } yield* _file diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart deleted file mode 100644 index ace2f602d45b..000000000000 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type/x_type.dart +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// TODO: should we be using this package or just extracting the important conversion data from it? -import 'package:mime_type/mime_type.dart'; - -/// A set of allowed XTypes -class XTypeGroup { - /// Creates a new group with the given label and file extensions. - const XTypeGroup({this.label, this.fileTypes}); - - /// The label for the grouping. On platforms that support selectable groups, - /// this will be visible to the user for selecting the group. - final String label; - - /// A list of allowed file extensions. E.g., ['png', 'jpg', 'jpeg', 'gif']. - /// - /// A null or empty list indicates any type is allowed. - final List fileTypes; -} - -/// A cross platform file type -class XType { - /// Variables to store type - String _mime; - String _extension; - - /// Default constructor - XType({ - String extension, - String mime, - }) : this._extension = extension, - this._mime = mime; - - /// Get the mime type from this XType - String get mime { - if (_mime == null) { - _mime = mimeFromExtension(_extension); - } - return _mime; - } - - /// Get the extension from this XType - String get extension { - if (_extension == null) { - _extension = extensionFromMime(_mime); - } - return _extension; - } -} \ No newline at end of file diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart new file mode 100644 index 000000000000..b38d3422ba92 --- /dev/null +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart @@ -0,0 +1,53 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// TODO: should we be using this package or just extracting the important conversion data from it? +import 'package:mime_type/mime_type.dart'; + +/// A set of allowed XTypes +class XTypeGroup { + /// Creates a new group with the given label and file extensions. + XTypeGroup({ + this.label, + this.extensions, + this.mimeTypes, + this.macUTIs, + this.webWildCards, + }) { + if ( + this.extensions == null && + this.mimeTypes == null && + this.macUTIs == null && + this.webWildCards == null + ) { + throw ArgumentError("At least one type must be provided for an XTypeGroup."); + } + } + + /// The 'name' or reference to this group of types + final String label; + + /// The extensions for this group + final List extensions; + + /// The MIME types for this group + final List mimeTypes; + + /// The UTIs for this group + final List macUTIs; + + /// The web wild cards for this group (ex: image/*, video/*) + final List webWildCards; +} + diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 59098d2e853e..8c10ae6aa05d 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -29,24 +29,35 @@ class FileSelectorPlugin extends FileSelectorPlatform { static void registerWith(Registrar registrar) { FileSelectorPlatform.instance = FileSelectorPlugin(); } + + void _verifyXTypeGroup(XTypeGroup group) { + if (group.extensions == null && group.mimeTypes == null && group.webWildCards == null) { + StateError("This XTypeGroup does not have types supported by the web implementation of loadFile."); + } + } /// Convert list of filter groups to a comma-separated string String _getStringFromFilterGroup (List acceptedTypes) { - List allExtensions = List(); + List allTypes = List(); for (XTypeGroup group in acceptedTypes ?? []) { - for (XType type in group.fileTypes ?? []) { - if (type.extension != null) { - String extension = type.extension; - if (extension[0] != '.') { - extension = '.' + extension; - } - allExtensions.add(extension); - } else if (type.mime != null) { - allExtensions.add(type.mime); + _verifyXTypeGroup(group); + + for (String mimeType in group.mimeTypes ?? []) { + allTypes.add(mimeType); + } + for (String extension in group.extensions ?? []) { + String ext = extension; + if (ext.isNotEmpty && [0] != '.') { + ext = '.' + ext; } + + allTypes.add(ext); + } + for (String webWildCard in group.webWildCards ?? []) { + allTypes.add(webWildCard); } } - return allExtensions?.where((e) => e.isNotEmpty)?.join(',') ?? ''; + return allTypes?.where((e) => e.isNotEmpty)?.join(',') ?? ''; } /// Creates a file input element with only the accept attribute diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index 89bb1d0904f1..39a1852ed55d 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -19,11 +19,16 @@ import 'dart:developer'; final String domElementId = '__file_selector_web_file_input'; final String xFileDomElementId = '__x_file_dom_element'; -final textGroup = XTypeGroup(label: 'test', - fileTypes: [ - XType(extension: 'json', mime: 'application/json'), - XType(extension: 'txt', mime: 'text/plain'), - ]); +final textGroup = XTypeGroup( + label: 'Text Files', + extensions: ['txt', 'json'], + mimeTypes: ['plain/text', 'application/json'], +); + +final badGroup = XTypeGroup( + label: 'Non-Web Group', + macUTIs: ['fake-uti'], +); final String expectedStringContents = 'Hello, world!'; final Uint8List bytes = utf8.encode(expectedStringContents); @@ -68,7 +73,7 @@ void main() { expect(result, isNotNull); expect(result.getAttribute('type'), 'file'); - expect(result.getAttribute('accept'), '.json,.txt'); + expect(result.getAttribute('accept'), 'plain/text,application/json,.txt,.json'); }); testWidgets('input element is clicked', (WidgetTester tester) async { @@ -119,6 +124,7 @@ void main() { expect(loadedFile.name, textFile.name); }); }); + group('loadFiles(..)', () { @@ -141,7 +147,7 @@ void main() { expect(result, isNotNull); expect(result.getAttribute('type'), 'file'); - expect(result.getAttribute('accept'), '.json,.txt'); + expect(result.getAttribute('accept'), 'plain/text,application/json,.txt,.json'); expect(result.multiple, true); }); From 3e6063f20164d79d98f464571063033ec889c45c Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 26 Aug 2020 17:04:35 -0700 Subject: [PATCH 107/151] Add confirmButtonText to all functions, acceptedTypeGroups to getSavePath Also mild refactor: move loadFile, loadFiles, getSavePath to top of file_selector_web.dart Update platform interface tests from picker to selector Fix web implementation tests to include mime types in expected value for file input element --- .../file_selector/lib/file_selector.dart | 10 ++- .../test/file_selector_test.dart | 16 +++-- .../method_channel_file_selector.dart | 5 ++ .../file_selector_interface.dart | 4 ++ .../file_picker_platform_interface_test.dart | 32 ++++----- .../lib/file_selector_web.dart | 70 ++++++++++--------- 6 files changed, 80 insertions(+), 57 deletions(-) diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index d533373e2855..788cf45fc47e 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -16,22 +16,26 @@ export 'package:file_selector_platform_interface/file_selector_platform_interfac Future loadFile({ List acceptedTypeGroups, String initialDirectory, + String confirmButtonText, }) { - return FileSelectorPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory); + return FileSelectorPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory, confirmButtonText: confirmButtonText); } /// Open file dialog for loading files and return a list of file paths Future> loadFiles({ List acceptedTypeGroups, String initialDirectory, + String confirmButtonText, }) { - return FileSelectorPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory); + return FileSelectorPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory, confirmButtonText: confirmButtonText); } /// Saves File to user's file system Future getSavePath({ + List acceptedTypeGroups, String initialDirectory, String suggestedName, + String confirmButtonText, }) async { - return FileSelectorPlatform.instance.getSavePath(initialDirectory: initialDirectory, suggestedName: suggestedName); + return FileSelectorPlatform.instance.getSavePath(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory, suggestedName: suggestedName, confirmButtonText: confirmButtonText); } \ No newline at end of file diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index 85cf78cbe776..837153e2e5f2 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -17,14 +17,18 @@ void main() { test('getSavePath', () async { String expectedPath = '/example/path'; + XTypeGroup typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); + when( mock.getSavePath( + acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', suggestedName: 'name', + confirmButtonText: 'save', ) ).thenAnswer((_) => Future.value(expectedPath)); - String result = await getSavePath(initialDirectory: 'dir', suggestedName: 'name'); + String result = await getSavePath(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', suggestedName: 'name', confirmButtonText: 'save'); expect(result, expectedPath); }); @@ -32,16 +36,17 @@ void main() { test('loadFile', () async { XFile file = XFile('path'); - XTypeGroup typeGroup = XTypeGroup(label: 'group', fileTypes: [ XType(extension: '.json', mime: 'application/json') ]); + XTypeGroup typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); when( mock.loadFile( acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', + confirmButtonText: 'load', ) ).thenAnswer((_) => Future.value(file)); - XFile result = await loadFile(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir'); + XFile result = await loadFile(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', confirmButtonText: 'load'); expect(result, isNotNull); }); @@ -49,16 +54,17 @@ void main() { test('loadFiles', () async { XFile file = XFile('path'); - XTypeGroup typeGroup = XTypeGroup(label: 'group', fileTypes: [ XType(extension: '.json', mime: 'application/json') ]); + XTypeGroup typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); when( mock.loadFiles( acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', + confirmButtonText: 'load', ) ).thenAnswer((_) => Future.value([file])); - List result = await loadFiles(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir'); + List result = await loadFiles(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', confirmButtonText: 'load'); expect(result, isNotNull); }); diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index 91ab40c9f840..fd4931b2e908 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -18,6 +18,7 @@ class MethodChannelFileSelector extends FileSelectorPlatform { Future loadFile({ List acceptedTypeGroups, String initialDirectory, + String confirmButtonText, }) { return _channel.invokeMethod( 'loadFile', @@ -33,6 +34,7 @@ class MethodChannelFileSelector extends FileSelectorPlatform { Future> loadFiles({ List acceptedTypeGroups, String initialDirectory, + String confirmButtonText, }) { return _channel.invokeMethod>( 'loadFiles', @@ -46,14 +48,17 @@ class MethodChannelFileSelector extends FileSelectorPlatform { /// Saves the file to user's Disk @override Future getSavePath({ + List acceptedTypeGroups, String initialDirectory, String suggestedName, + String confirmButtonText, }) async { return _channel.invokeMethod( 'saveFile', { 'initialDirectory': initialDirectory, 'suggestedName': suggestedName, + }, ); } diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index 94191fea7a0b..94213f5ba944 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -44,6 +44,7 @@ abstract class FileSelectorPlatform extends PlatformInterface { Future loadFile({ List acceptedTypeGroups, String initialDirectory, + String confirmButtonText, }) { throw UnimplementedError('loadFile() has not been implemented.'); } @@ -52,14 +53,17 @@ abstract class FileSelectorPlatform extends PlatformInterface { Future> loadFiles({ List acceptedTypeGroups, String initialDirectory, + String confirmButtonText, }) { throw UnimplementedError('loadFile() has not been implemented.'); } /// Open file dialog for saving files and return a file path at which to save Future getSavePath({ + List acceptedTypeGroups, String initialDirectory, String suggestedName, + String confirmButtonText, }) { throw UnimplementedError('saveFile() has not been implemented.'); } diff --git a/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart b/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart index e454ac6ca951..a1859fe054aa 100644 --- a/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart @@ -7,41 +7,39 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:file_picker_platform_interface/file_selector_platform_interface.dart'; -import 'package:file_picker_platform_interface/src/method_channel/method_channel_file_selector.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:file_selector_platform_interface/src/method_channel/method_channel_file_selector.dart'; void main() { - group('$FilePickerPlatform', () { - test('$MethodChannelFilePicker() is the default instance', () { - expect(FilePickerPlatform.instance, - isInstanceOf()); + group('$FileSelectorPlatform', () { + test('$MethodChannelFileSelector() is the default instance', () { + expect(FileSelectorPlatform.instance, + isInstanceOf()); }); test('Cannot be implemented with `implements`', () { expect(() { - FilePickerPlatform.instance = ImplementsFilePickerPlatform(); + FileSelectorPlatform.instance = ImplementsFileSelectorPlatform(); }, throwsA(isInstanceOf())); }); test('Can be mocked with `implements`', () { - final FilePickerPlatformMock mock = FilePickerPlatformMock(); - FilePickerPlatform.instance = mock; + final FileSelectorPlatformMock mock = FileSelectorPlatformMock(); + FileSelectorPlatform.instance = mock; }); test('Can be extended', () { - FilePickerPlatform.instance = ExtendsFilePickerPlatform(); + FileSelectorPlatform.instance = ExtendsFileSelectorPlatform(); }); }); - - } -class FilePickerPlatformMock extends Mock +class FileSelectorPlatformMock extends Mock with MockPlatformInterfaceMixin - implements FilePickerPlatform {} + implements FileSelectorPlatform {} -class ImplementsFilePickerPlatform extends Mock - implements FilePickerPlatform {} +class ImplementsFileSelectorPlatform extends Mock + implements FileSelectorPlatform {} -class ExtendsFilePickerPlatform extends FilePickerPlatform {} \ No newline at end of file +class ExtendsFileSelectorPlatform extends FileSelectorPlatform {} \ No newline at end of file diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 8c10ae6aa05d..30b3599d2551 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -12,6 +12,44 @@ final String _kFileSelectorInputsDomId = '__file_selector_web_file_input'; /// /// This class implements the `package:file_selector` functionality for the web. class FileSelectorPlugin extends FileSelectorPlatform { + + /// Open file dialog for loading files and return a XFile + @override + Future loadFile({ + List acceptedTypeGroups, + String initialDirectory, + String confirmButtonText, + }) { + Completer _completer = Completer(); + _loadFileHelper(false, acceptedTypeGroups).then((list) { + _completer.complete(list.first); + }) + .catchError((err) { + _completer.completeError(err); + }); + + return _completer.future; + } + + /// Open file dialog for loading files and return a XFile + @override + Future> loadFiles({ + List acceptedTypeGroups, + String initialDirectory, + String confirmButtonText, + }) { + return _loadFileHelper(true, acceptedTypeGroups); + } + + @override + Future getSavePath({ + List acceptedTypeGroups, + String initialDirectory, + String suggestedName, + String confirmButtonText, + }) => Future.value(); + + Element _target; final FileSelectorPluginTestOverrides _overrides; bool get _hasTestOverrides => _overrides != null; @@ -166,38 +204,6 @@ class FileSelectorPlugin extends FileSelectorPlatform { return getFilesWhenReady(element); } - - /// Open file dialog for loading files and return a XFile - @override - Future loadFile({ - List acceptedTypeGroups, - String initialDirectory, - }) { - Completer _completer = Completer(); - _loadFileHelper(false, acceptedTypeGroups).then((list) { - _completer.complete(list.first); - }) - .catchError((err) { - _completer.completeError(err); - }); - - return _completer.future; - } - - /// Open file dialog for loading files and return a XFile - @override - Future> loadFiles({ - List acceptedTypeGroups, - String initialDirectory, - }) { - return _loadFileHelper(true, acceptedTypeGroups); - } - - @override - Future getSavePath({ - String initialDirectory, - String suggestedName, - }) => Future.value(); } /// Overrides some functions to allow testing From e97182d30f3facfae510487902b86bc05a5e1107 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 13:27:55 -0700 Subject: [PATCH 108/151] Fix `flutter analyze` issues --- .../file_selector/example/lib/main.dart | 23 ++++++----------- .../example/test/widget_test.dart | 5 ---- .../file_selector/lib/file_selector.dart | 3 --- .../test/file_selector_test.dart | 1 - .../method_channel_file_selector.dart | 2 -- .../file_selector_interface.dart | 1 - .../lib/src/types/x_file/base.dart | 2 +- .../lib/src/types/x_file/html.dart | 25 +++++++++++++------ .../lib/src/types/x_file/io.dart | 15 ++++++----- .../src/types/x_type_group/x_type_group.dart | 1 - .../file_picker_platform_interface_test.dart | 1 - .../lib/file_selector_web.dart | 5 ++-- .../test/test_driver/web_e2e.dart | 22 ++++++---------- 13 files changed, 42 insertions(+), 64 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 0c79faa72fc9..ffb85fa8e7fa 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -19,8 +19,8 @@ class MyApp extends StatelessWidget { ), home: MyHomePage(title: 'File Selector Demo Home Page'), routes: { - '/save' : (context) => SaveTest(title: "Save Example"), - '/load' : (context) => LoadTest(title: "Load Example"), + '/save' : (context) => SaveTest(), + '/load' : (context) => LoadTest(), }, ); } @@ -28,10 +28,8 @@ class MyApp extends StatelessWidget { /// Page for showing an example of saving with file_selector class SaveTest extends StatefulWidget { - SaveTest({Key key, this.title}) : super(key: key); - - /// Title of Home Page - final String title; + /// Constructor for the SaveTest page + SaveTest({Key key}) : super(key: key); @override _SaveTestState createState() => _SaveTestState(); @@ -40,7 +38,6 @@ class SaveTest extends StatefulWidget { class _SaveTestState extends State { final TextEditingController _fileController = TextEditingController(); final TextEditingController _nameController = TextEditingController(); - final TextEditingController _extensionController = TextEditingController(); @override void dispose() { @@ -70,7 +67,7 @@ class _SaveTestState extends State { return Scaffold( appBar: AppBar( - title: Text(widget.title), + title: Text("Save Example"), ), body: Center( child: Column( @@ -113,19 +110,13 @@ class _SaveTestState extends State { /// Screen that shows an example of loadFile(s) class LoadTest extends StatefulWidget { /// Default constructor - LoadTest({Key key, this.title}) : super(key: key); - - /// Title of Home Page - final String title; - + LoadTest({Key key}) : super(key: key); @override _LoadTestState createState() => _LoadTestState(); } class _LoadTestState extends State { - final TextEditingController _extensionController = TextEditingController(); - void _onLoadImageFile() async { XTypeGroup typeGroup = XTypeGroup(label: 'images', extensions: [ 'jpg', 'png' ]); @@ -159,7 +150,7 @@ class _LoadTestState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text(widget.title), + title: Text("Load Example"), ), body: Center( child: Column( diff --git a/packages/file_selector/file_selector/example/test/widget_test.dart b/packages/file_selector/file_selector/example/test/widget_test.dart index c9a60e1f271f..baa589079551 100644 --- a/packages/file_selector/file_selector/example/test/widget_test.dart +++ b/packages/file_selector/file_selector/example/test/widget_test.dart @@ -5,11 +5,6 @@ // 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() { } diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index 788cf45fc47e..fc770c707422 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -3,15 +3,12 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:typed_data'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; export 'package:file_selector_platform_interface/file_selector_platform_interface.dart' show XFile, XTypeGroup; -/// NEW API - /// Open file dialog for loading files and return a file path Future loadFile({ List acceptedTypeGroups, diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index 837153e2e5f2..88015a46d72b 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -4,7 +4,6 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter/material.dart'; import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:file_selector/file_selector.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index fd4931b2e908..5b94c3c0892a 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:typed_data'; - import 'package:flutter/services.dart'; import '../platform_interface/file_selector_interface.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index 94213f5ba944..2e7d954d0860 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:typed_data'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart index be9db430e924..d80677a3fd10 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart @@ -1,6 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; -import '../x_type_group/x_type_group.dart'; + /// The interface for a XFile. /// diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index 7a3cde9ca7fb..6768fde43b9e 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -52,22 +52,31 @@ class XFile extends XFileBase { this.name, int length, DateTime lastModified, + this.path, @visibleForTesting XFileTestOverrides overrides, }) : _data = bytes, _length = length, _overrides = overrides, _lastModified = lastModified, - super('') { - Blob blob; - if (mimeType == null) { - blob = Blob([bytes]); - } else { - blob = Blob([bytes], mimeType); + super(path) { + if (path == null) { + Blob blob; + if (mimeType == null) { + blob = Blob([bytes]); + } else { + blob = Blob([bytes], mimeType); + } + this.path = Url.createObjectUrl(blob); } - this.path = Url.createObjectUrl(blob); } - + @override + Future lastModified() async { + if (_lastModified != null) { + return Future.value(_lastModified); + } + return null; + } Future get _bytes async { if (_data != null) { diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart index 536b4456eca2..a8e1b50f7d9d 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart @@ -4,8 +4,6 @@ import 'dart:typed_data'; import './base.dart'; -import '../types.dart'; - /// A XFile backed by a dart:io File. class XFile extends XFileBase { final File _file; @@ -30,12 +28,13 @@ class XFile extends XFileBase { super(path); /// Construct an XFile from its data - XFile.fromData(Uint8List bytes, { - this.mimeType, - String path, - String name, - int length, - DateTime lastModified, + XFile.fromData( + Uint8List bytes, { + this.mimeType, + String path, + String name, + int length, + DateTime lastModified, }): _bytes = bytes, _file = File(path), _length = length, diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart index b38d3422ba92..5c06302cd365 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart @@ -13,7 +13,6 @@ // limitations under the License. // TODO: should we be using this package or just extracting the important conversion data from it? -import 'package:mime_type/mime_type.dart'; /// A set of allowed XTypes class XTypeGroup { diff --git a/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart b/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart index a1859fe054aa..6b1367143c99 100644 --- a/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'package:mockito/mockito.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 30b3599d2551..804d485bd896 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:html'; -import 'dart:typed_data'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; @@ -85,7 +84,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { } for (String extension in group.extensions ?? []) { String ext = extension; - if (ext.isNotEmpty && [0] != '.') { + if (ext.isNotEmpty && ext[0] != '.') { ext = '.' + ext; } @@ -133,7 +132,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { int length = file.size; DateTime modified = file.lastModifiedDate.add(timeZoneOffset); - xFiles.add(XFile(url, name: name, lastModified: modified)); + xFiles.add(XFile(url, name: name, lastModified: modified, length: length)); } return xFiles; diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index 39a1852ed55d..7317338cc466 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -2,8 +2,6 @@ import 'dart:async'; import 'package:e2e/e2e.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; - import 'dart:convert'; import 'dart:typed_data'; @@ -12,10 +10,6 @@ import 'dart:html'; import 'package:file_selector_web/file_selector_web.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; -import 'package:platform_detect/test_utils.dart' as platform; - -import 'dart:developer'; - final String domElementId = '__file_selector_web_file_input'; final String xFileDomElementId = '__x_file_dom_element'; @@ -46,14 +40,14 @@ void main() { FileSelectorPlugin plugin; - testWidgets('Create a DOM container', (WidgetTester tester) { - plugin = FileSelectorPlugin(); + group('loadFile(..)', () { + testWidgets('creates a DOM container', (WidgetTester tester) async { + plugin = FileSelectorPlugin(); - final result = querySelector('#${domElementId}'); - expect(result, isNotNull); - }); + final result = querySelector('#${domElementId}'); + expect(result, isNotNull); + }); - group('loadFile(..)', () { testWidgets('creates correct input element', (WidgetTester tester) async { final overrides = FileSelectorPluginTestOverrides( getFilesWhenReady: (_) => Future.value([ XFile('path') ]), @@ -91,7 +85,7 @@ void main() { bool clicked = false; mockInput.onClick.listen((event) => clicked = true); - final file = await plugin.loadFile(acceptedTypeGroups: [ textGroup ]); + await plugin.loadFile(acceptedTypeGroups: [ textGroup ]); expect(clicked, true); }); @@ -168,7 +162,7 @@ void main() { bool clicked = false; mockInput.onClick.listen((event) => clicked = true); - final file = await plugin.loadFiles(acceptedTypeGroups: [ textGroup ]); + await plugin.loadFiles(acceptedTypeGroups: [ textGroup ]); expect(clicked, true); }); From a1bc749ea9b8f013943248e48998067e94920c39 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 13:28:20 -0700 Subject: [PATCH 109/151] Update unsupported.dart --- .../lib/src/types/x_file/unsupported.dart | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart index 8acdeb329b54..bf3d46d00e77 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart @@ -1,4 +1,5 @@ import 'dart:typed_data'; +import 'package:meta/meta.dart'; import './base.dart'; @@ -15,23 +16,38 @@ class XFile extends XFileBase { /// `path` of the file doesn't match what the user sees when selecting it /// (like in web) XFile( - String path, { - String name, - int length, - Uint8List bytes, - }) : super(path) { + String path, { + String mimeType, + String name, + int length, + Uint8List bytes, + DateTime lastModified, + @visibleForTesting XFileTestOverrides overrides, + }) : super(path) { throw UnimplementedError( 'XFile is not available in your current platform.'); } /// Construct a XFile object from its data XFile.fromData( - Uint8List data, { - String path, + Uint8List bytes, { + String mimeType, String name, int length, + DateTime lastModified, + String path, + @visibleForTesting XFileTestOverrides overrides, }) : super(path) { throw UnimplementedError( 'XFile is not available in your current platform.'); } } + +/// Overrides some functions of XFile for testing purposes +@visibleForTesting class XFileTestOverrides { + /// For overriding the creation of the file input element. + dynamic Function(String href, String suggestedName) createAnchorElement; + + /// Default constructor for overrides + XFileTestOverrides({this.createAnchorElement}); +} \ No newline at end of file From f58d402bc74dffa72d9080513b3c36095d3c6a58 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 13:35:04 -0700 Subject: [PATCH 110/151] Format code --- .../file_selector/example/lib/main.dart | 43 +++++------ .../example/test/widget_test.dart | 4 +- .../file_selector/lib/file_selector.dart | 20 +++-- .../test/file_selector_test.dart | 66 ++++++++-------- .../method_channel_file_selector.dart | 7 +- .../file_selector_interface.dart | 3 - .../lib/src/types/x_file/base.dart | 3 +- .../lib/src/types/x_file/html.dart | 38 +++++----- .../lib/src/types/x_file/io.dart | 27 ++++--- .../lib/src/types/x_file/unsupported.dart | 37 ++++----- .../lib/src/types/x_file/x_file.dart | 2 +- .../src/types/x_type_group/x_type_group.dart | 14 ++-- .../file_picker_platform_interface_test.dart | 3 +- .../lib/file_selector_web.dart | 70 +++++++++-------- .../file_selector_web/test/lib/main.dart | 1 - .../test/test_driver/web_e2e.dart | 75 ++++++++++--------- 16 files changed, 210 insertions(+), 203 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index ffb85fa8e7fa..06ae8125e4d6 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -19,8 +19,8 @@ class MyApp extends StatelessWidget { ), home: MyHomePage(title: 'File Selector Demo Home Page'), routes: { - '/save' : (context) => SaveTest(), - '/load' : (context) => LoadTest(), + '/save': (context) => SaveTest(), + '/load': (context) => LoadTest(), }, ); } @@ -56,7 +56,8 @@ class _SaveTestState extends State { if (_nameController.text == '') { new_file = XFile.fromData(data, mimeType: 'text/plain'); } else { - new_file = XFile.fromData(data, mimeType: 'text/plain', name: _nameController.text); + new_file = XFile.fromData(data, + mimeType: 'text/plain', name: _nameController.text); } new_file.saveTo(path); @@ -64,7 +65,6 @@ class _SaveTestState extends State { @override Widget build(BuildContext context) { - return Scaffold( appBar: AppBar( title: Text("Save Example"), @@ -98,7 +98,7 @@ class _SaveTestState extends State { SizedBox(height: 10), RaisedButton( child: Text('Press to save a text file'), - onPressed: () => { _saveFile() }, + onPressed: () => {_saveFile()}, ), ], ), @@ -118,23 +118,21 @@ class LoadTest extends StatefulWidget { class _LoadTestState extends State { void _onLoadImageFile() async { + XTypeGroup typeGroup = + XTypeGroup(label: 'images', extensions: ['jpg', 'png']); - XTypeGroup typeGroup = XTypeGroup(label: 'images', extensions: [ 'jpg', 'png' ]); - - XFile file = await loadFile(acceptedTypeGroups: [ typeGroup ]); + XFile file = await loadFile(acceptedTypeGroups: [typeGroup]); await showDialog( - context: context, - builder: (context) { - return ImageDisplay(file: file); - } - ); - - + context: context, + builder: (context) { + return ImageDisplay(file: file); + }); } void _onLoadTextFile() async { - XTypeGroup typeGroup = XTypeGroup(label: 'images', extensions: [ 'txt', 'json' ]); + XTypeGroup typeGroup = + XTypeGroup(label: 'images', extensions: ['txt', 'json']); XFile file = await loadFile(acceptedTypeGroups: [typeGroup]); @@ -142,8 +140,7 @@ class _LoadTestState extends State { context: context, builder: (context) { return TextDisplay(file: file); - } - ); + }); } @override @@ -204,7 +201,8 @@ class _TextDisplayState extends State { content: Scrollbar( child: SingleChildScrollView( child: Text( - fileContents ?? 'Loading file contents...\nThis may take a while if your file is large.', + fileContents ?? + 'Loading file contents...\nThis may take a while if your file is large.', ), ), ), @@ -233,7 +231,6 @@ class ImageDisplay extends StatefulWidget { } class _ImageDisplayState extends State { - @override void initState() { super.initState(); @@ -244,7 +241,6 @@ class _ImageDisplayState extends State { return AlertDialog( title: Text(widget.file.name), content: Image.network(widget.file.path), - actions: [ FlatButton( child: const Text('Close'), @@ -257,11 +253,8 @@ class _ImageDisplayState extends State { } } - - /// Home Page of the application class MyHomePage extends StatefulWidget { - /// Constructor for MyHomePage MyHomePage({Key key, this.title}) : super(key: key); @@ -274,7 +267,6 @@ class MyHomePage extends StatefulWidget { /// State of Home Page class _MyHomePageState extends State { - @override Widget build(BuildContext context) { return Scaffold( @@ -299,5 +291,4 @@ class _MyHomePageState extends State { ), ); } - } diff --git a/packages/file_selector/file_selector/example/test/widget_test.dart b/packages/file_selector/file_selector/example/test/widget_test.dart index baa589079551..570e0e4768dc 100644 --- a/packages/file_selector/file_selector/example/test/widget_test.dart +++ b/packages/file_selector/file_selector/example/test/widget_test.dart @@ -5,6 +5,4 @@ // 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. -void main() { - -} +void main() {} diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index fc770c707422..659b4e87fe4f 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -7,7 +7,7 @@ import 'dart:async'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; export 'package:file_selector_platform_interface/file_selector_platform_interface.dart' - show XFile, XTypeGroup; + show XFile, XTypeGroup; /// Open file dialog for loading files and return a file path Future loadFile({ @@ -15,7 +15,10 @@ Future loadFile({ String initialDirectory, String confirmButtonText, }) { - return FileSelectorPlatform.instance.loadFile(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory, confirmButtonText: confirmButtonText); + return FileSelectorPlatform.instance.loadFile( + acceptedTypeGroups: acceptedTypeGroups, + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText); } /// Open file dialog for loading files and return a list of file paths @@ -24,7 +27,10 @@ Future> loadFiles({ String initialDirectory, String confirmButtonText, }) { - return FileSelectorPlatform.instance.loadFiles(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory, confirmButtonText: confirmButtonText); + return FileSelectorPlatform.instance.loadFiles( + acceptedTypeGroups: acceptedTypeGroups, + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText); } /// Saves File to user's file system @@ -34,5 +40,9 @@ Future getSavePath({ String suggestedName, String confirmButtonText, }) async { - return FileSelectorPlatform.instance.getSavePath(acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory, suggestedName: suggestedName, confirmButtonText: confirmButtonText); -} \ No newline at end of file + return FileSelectorPlatform.instance.getSavePath( + acceptedTypeGroups: acceptedTypeGroups, + initialDirectory: initialDirectory, + suggestedName: suggestedName, + confirmButtonText: confirmButtonText); +} diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index 88015a46d72b..81b1772fb3bc 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; @@ -16,18 +15,21 @@ void main() { test('getSavePath', () async { String expectedPath = '/example/path'; - XTypeGroup typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); + XTypeGroup typeGroup = + XTypeGroup(label: 'group', extensions: ['jpg', 'png']); - when( - mock.getSavePath( - acceptedTypeGroups: [typeGroup], - initialDirectory: 'dir', - suggestedName: 'name', - confirmButtonText: 'save', - ) - ).thenAnswer((_) => Future.value(expectedPath)); + when(mock.getSavePath( + acceptedTypeGroups: [typeGroup], + initialDirectory: 'dir', + suggestedName: 'name', + confirmButtonText: 'save', + )).thenAnswer((_) => Future.value(expectedPath)); - String result = await getSavePath(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', suggestedName: 'name', confirmButtonText: 'save'); + String result = await getSavePath( + acceptedTypeGroups: [typeGroup], + initialDirectory: 'dir', + suggestedName: 'name', + confirmButtonText: 'save'); expect(result, expectedPath); }); @@ -35,17 +37,19 @@ void main() { test('loadFile', () async { XFile file = XFile('path'); - XTypeGroup typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); + XTypeGroup typeGroup = + XTypeGroup(label: 'group', extensions: ['jpg', 'png']); - when( - mock.loadFile( - acceptedTypeGroups: [typeGroup], - initialDirectory: 'dir', - confirmButtonText: 'load', - ) - ).thenAnswer((_) => Future.value(file)); + when(mock.loadFile( + acceptedTypeGroups: [typeGroup], + initialDirectory: 'dir', + confirmButtonText: 'load', + )).thenAnswer((_) => Future.value(file)); - XFile result = await loadFile(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', confirmButtonText: 'load'); + XFile result = await loadFile( + acceptedTypeGroups: [typeGroup], + initialDirectory: 'dir', + confirmButtonText: 'load'); expect(result, isNotNull); }); @@ -53,17 +57,19 @@ void main() { test('loadFiles', () async { XFile file = XFile('path'); - XTypeGroup typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); + XTypeGroup typeGroup = + XTypeGroup(label: 'group', extensions: ['jpg', 'png']); - when( - mock.loadFiles( - acceptedTypeGroups: [typeGroup], - initialDirectory: 'dir', - confirmButtonText: 'load', - ) - ).thenAnswer((_) => Future.value([file])); + when(mock.loadFiles( + acceptedTypeGroups: [typeGroup], + initialDirectory: 'dir', + confirmButtonText: 'load', + )).thenAnswer((_) => Future.value([file])); - List result = await loadFiles(acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', confirmButtonText: 'load'); + List result = await loadFiles( + acceptedTypeGroups: [typeGroup], + initialDirectory: 'dir', + confirmButtonText: 'load'); expect(result, isNotNull); }); @@ -71,4 +77,4 @@ void main() { class MockFileSelector extends Mock with MockPlatformInterfaceMixin - implements FileSelectorPlatform {} \ No newline at end of file + implements FileSelectorPlatform {} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index 5b94c3c0892a..097075c96287 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -20,7 +20,7 @@ class MethodChannelFileSelector extends FileSelectorPlatform { }) { return _channel.invokeMethod( 'loadFile', - { + { 'acceptedTypes': acceptedTypeGroups, 'initialDirectory': initialDirectory, }, @@ -36,7 +36,7 @@ class MethodChannelFileSelector extends FileSelectorPlatform { }) { return _channel.invokeMethod>( 'loadFiles', - { + { 'acceptedTypes': acceptedTypeGroups, 'initialDirectory': initialDirectory, }, @@ -53,10 +53,9 @@ class MethodChannelFileSelector extends FileSelectorPlatform { }) async { return _channel.invokeMethod( 'saveFile', - { + { 'initialDirectory': initialDirectory, 'suggestedName': suggestedName, - }, ); } diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index 2e7d954d0860..66b14c75efd8 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -9,9 +9,6 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import '../method_channel/method_channel_file_selector.dart'; - - - /// The interface that implementations of file_picker must implement. /// /// Platform implementations should extend this class rather than implement it as `file_picker` diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart index d80677a3fd10..7ea050ff28db 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; - /// The interface for a XFile. /// /// A XFile is a container that wraps the path of a selected @@ -80,4 +79,4 @@ abstract class XFileBase { Future lastModified() { throw UnimplementedError('openRead() has not been implemented.'); } -} \ No newline at end of file +} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index 6768fde43b9e..fbbe0a139dc1 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -32,14 +32,14 @@ class XFile extends XFileBase { /// `name` needs to be passed from the outside, since we only have /// access to it while we create the ObjectUrl. XFile( - this.path, { - this.mimeType, - this.name, - int length, - Uint8List bytes, - DateTime lastModified, - @visibleForTesting XFileTestOverrides overrides, - }) : _data = bytes, + this.path, { + this.mimeType, + this.name, + int length, + Uint8List bytes, + DateTime lastModified, + @visibleForTesting XFileTestOverrides overrides, + }) : _data = bytes, _length = length, _overrides = overrides, _lastModified = lastModified, @@ -47,14 +47,14 @@ class XFile extends XFileBase { /// Construct an XFile from its data XFile.fromData( - Uint8List bytes, { - this.mimeType, - this.name, - int length, - DateTime lastModified, - this.path, - @visibleForTesting XFileTestOverrides overrides, - }) : _data = bytes, + Uint8List bytes, { + this.mimeType, + this.name, + int length, + DateTime lastModified, + this.path, + @visibleForTesting XFileTestOverrides overrides, + }) : _data = bytes, _length = length, _overrides = overrides, _lastModified = lastModified, @@ -148,8 +148,7 @@ class XFile extends XFileBase { Element _ensureInitialized(String id) { var target = querySelector('#${id}'); if (target == null) { - final Element targetElement = - Element.tag('flt-x-file')..id = id; + final Element targetElement = Element.tag('flt-x-file')..id = id; querySelector('body').children.add(targetElement); target = targetElement; @@ -158,7 +157,6 @@ class XFile extends XFileBase { } } - /// Overrides some functions to allow testing @visibleForTesting class XFileTestOverrides { @@ -167,4 +165,4 @@ class XFileTestOverrides { /// Default constructor for overrides XFileTestOverrides({this.createAnchorElement}); -} \ No newline at end of file +} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart index a8e1b50f7d9d..bd8470a1d8ca 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart @@ -16,26 +16,25 @@ class XFile extends XFileBase { /// Construct a XFile object backed by a dart:io File. XFile( String path, { - this.mimeType, - String name, - int length, - Uint8List bytes, - DateTime lastModified, - }) - : _file = File(path), + this.mimeType, + String name, + int length, + Uint8List bytes, + DateTime lastModified, + }) : _file = File(path), _bytes = null, _lastModified = lastModified, super(path); /// Construct an XFile from its data XFile.fromData( - Uint8List bytes, { - this.mimeType, - String path, - String name, - int length, - DateTime lastModified, - }): _bytes = bytes, + Uint8List bytes, { + this.mimeType, + String path, + String name, + int length, + DateTime lastModified, + }) : _bytes = bytes, _file = File(path), _length = length, _lastModified = lastModified, diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart index bf3d46d00e77..f5fe388e0899 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart @@ -16,38 +16,39 @@ class XFile extends XFileBase { /// `path` of the file doesn't match what the user sees when selecting it /// (like in web) XFile( - String path, { - String mimeType, - String name, - int length, - Uint8List bytes, - DateTime lastModified, - @visibleForTesting XFileTestOverrides overrides, - }) : super(path) { + String path, { + String mimeType, + String name, + int length, + Uint8List bytes, + DateTime lastModified, + @visibleForTesting XFileTestOverrides overrides, + }) : super(path) { throw UnimplementedError( 'XFile is not available in your current platform.'); } /// Construct a XFile object from its data XFile.fromData( - Uint8List bytes, { - String mimeType, - String name, - int length, - DateTime lastModified, - String path, - @visibleForTesting XFileTestOverrides overrides, - }) : super(path) { + Uint8List bytes, { + String mimeType, + String name, + int length, + DateTime lastModified, + String path, + @visibleForTesting XFileTestOverrides overrides, + }) : super(path) { throw UnimplementedError( 'XFile is not available in your current platform.'); } } /// Overrides some functions of XFile for testing purposes -@visibleForTesting class XFileTestOverrides { +@visibleForTesting +class XFileTestOverrides { /// For overriding the creation of the file input element. dynamic Function(String href, String suggestedName) createAnchorElement; /// Default constructor for overrides XFileTestOverrides({this.createAnchorElement}); -} \ No newline at end of file +} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart index 72d156cc58fd..f966a7c9a3aa 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart @@ -1,3 +1,3 @@ export 'unsupported.dart' if (dart.library.html) 'html.dart' - if (dart.library.io) 'io.dart'; \ No newline at end of file + if (dart.library.io) 'io.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart index 5c06302cd365..8b02af8ea24e 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart @@ -24,13 +24,12 @@ class XTypeGroup { this.macUTIs, this.webWildCards, }) { - if ( - this.extensions == null && - this.mimeTypes == null && - this.macUTIs == null && - this.webWildCards == null - ) { - throw ArgumentError("At least one type must be provided for an XTypeGroup."); + if (this.extensions == null && + this.mimeTypes == null && + this.macUTIs == null && + this.webWildCards == null) { + throw ArgumentError( + "At least one type must be provided for an XTypeGroup."); } } @@ -49,4 +48,3 @@ class XTypeGroup { /// The web wild cards for this group (ex: image/*, video/*) final List webWildCards; } - diff --git a/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart b/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart index 6b1367143c99..465900fa429a 100644 --- a/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart @@ -33,7 +33,6 @@ void main() { }); } - class FileSelectorPlatformMock extends Mock with MockPlatformInterfaceMixin implements FileSelectorPlatform {} @@ -41,4 +40,4 @@ class FileSelectorPlatformMock extends Mock class ImplementsFileSelectorPlatform extends Mock implements FileSelectorPlatform {} -class ExtendsFileSelectorPlatform extends FileSelectorPlatform {} \ No newline at end of file +class ExtendsFileSelectorPlatform extends FileSelectorPlatform {} diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 804d485bd896..02e5a1a2e79c 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -11,7 +11,6 @@ final String _kFileSelectorInputsDomId = '__file_selector_web_file_input'; /// /// This class implements the `package:file_selector` functionality for the web. class FileSelectorPlugin extends FileSelectorPlatform { - /// Open file dialog for loading files and return a XFile @override Future loadFile({ @@ -22,8 +21,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { Completer _completer = Completer(); _loadFileHelper(false, acceptedTypeGroups).then((list) { _completer.complete(list.first); - }) - .catchError((err) { + }).catchError((err) { _completer.completeError(err); }); @@ -46,14 +44,14 @@ class FileSelectorPlugin extends FileSelectorPlatform { String initialDirectory, String suggestedName, String confirmButtonText, - }) => Future.value(); - - + }) => + Future.value(); + Element _target; final FileSelectorPluginTestOverrides _overrides; bool get _hasTestOverrides => _overrides != null; - /// Default constructor, initializes _target to a DOM element that we can use + /// Default constructor, initializes _target to a DOM element that we can use /// to host HTML elements. /// overrides parameter allows for testing to override functions FileSelectorPlugin({ @@ -68,13 +66,16 @@ class FileSelectorPlugin extends FileSelectorPlatform { } void _verifyXTypeGroup(XTypeGroup group) { - if (group.extensions == null && group.mimeTypes == null && group.webWildCards == null) { - StateError("This XTypeGroup does not have types supported by the web implementation of loadFile."); + if (group.extensions == null && + group.mimeTypes == null && + group.webWildCards == null) { + StateError( + "This XTypeGroup does not have types supported by the web implementation of loadFile."); } } - + /// Convert list of filter groups to a comma-separated string - String _getStringFromFilterGroup (List acceptedTypes) { + String _getStringFromFilterGroup(List acceptedTypes) { List allTypes = List(); for (XTypeGroup group in acceptedTypes ?? []) { _verifyXTypeGroup(group); @@ -82,12 +83,12 @@ class FileSelectorPlugin extends FileSelectorPlatform { for (String mimeType in group.mimeTypes ?? []) { allTypes.add(mimeType); } - for (String extension in group.extensions ?? []) { + for (String extension in group.extensions ?? []) { String ext = extension; if (ext.isNotEmpty && ext[0] != '.') { ext = '.' + ext; } - + allTypes.add(ext); } for (String webWildCard in group.webWildCards ?? []) { @@ -99,11 +100,12 @@ class FileSelectorPlugin extends FileSelectorPlatform { /// Creates a file input element with only the accept attribute @visibleForTesting - FileUploadInputElement createFileInputElement(String accepted, bool multiple) { + FileUploadInputElement createFileInputElement( + String accepted, bool multiple) { if (_hasTestOverrides && _overrides.createFileInputElement != null) { return _overrides.createFileInputElement(accepted, multiple); } - + final FileUploadInputElement element = FileUploadInputElement(); if (accepted.isNotEmpty) { element.accept = accepted; @@ -121,9 +123,9 @@ class FileSelectorPlugin extends FileSelectorPlatform { element.click(); } - List _getXFilesFromFiles (List files) { + List _getXFilesFromFiles(List files) { List xFiles = List(); - + Duration timeZoneOffset = DateTime.now().timeZoneOffset; for (File file in files) { @@ -132,7 +134,8 @@ class FileSelectorPlugin extends FileSelectorPlatform { int length = file.size; DateTime modified = file.lastModifiedDate.add(timeZoneOffset); - xFiles.add(XFile(url, name: name, lastModified: modified, length: length)); + xFiles + .add(XFile(url, name: name, lastModified: modified, length: length)); } return xFiles; @@ -141,18 +144,18 @@ class FileSelectorPlugin extends FileSelectorPlatform { /// Getter for retrieving files from an input element @visibleForTesting List getFilesFromInputElement(InputElement element) { - if(_hasTestOverrides && _overrides.getFilesFromInputElement != null) { + if (_hasTestOverrides && _overrides.getFilesFromInputElement != null) { return _overrides.getFilesFromInputElement(element); } return element?.files ?? []; } - + /// Listen for file input element to change and retrieve files when /// this happens. @visibleForTesting - Future> getFilesWhenReady(InputElement element) { - if(_hasTestOverrides && _overrides.getFilesWhenReady != null) { + Future> getFilesWhenReady(InputElement element) { + if (_hasTestOverrides && _overrides.getFilesWhenReady != null) { return _overrides.getFilesWhenReady(element); } @@ -182,22 +185,24 @@ class FileSelectorPlugin extends FileSelectorPlatform { Element _ensureInitialized(String id) { var target = querySelector('#${id}'); if (target == null) { - final Element targetElement = - Element.tag('flt-file-picker-inputs')..id = id; + final Element targetElement = Element.tag('flt-file-picker-inputs') + ..id = id; querySelector('body').children.add(targetElement); target = targetElement; } return target; } - + /// NEW API - + /// Load Helper - Future> _loadFileHelper (bool multiple, List acceptedTypes) { - final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); + Future> _loadFileHelper( + bool multiple, List acceptedTypes) { + final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); - final FileUploadInputElement element = createFileInputElement(acceptedTypeString, multiple); + final FileUploadInputElement element = + createFileInputElement(acceptedTypeString, multiple); _addElementToDomAndClick(element); @@ -217,5 +222,8 @@ class FileSelectorPluginTestOverrides { /// For overriding waiting for the files to be ready. Useful for testing so we do not hang here. Future> Function(InputElement input) getFilesWhenReady; - FileSelectorPluginTestOverrides({this.createFileInputElement, this.getFilesFromInputElement, this.getFilesWhenReady}); -} \ No newline at end of file + FileSelectorPluginTestOverrides( + {this.createFileInputElement, + this.getFilesFromInputElement, + this.getFilesWhenReady}); +} diff --git a/packages/file_selector/file_selector_web/test/lib/main.dart b/packages/file_selector/file_selector_web/test/lib/main.dart index 4c55f6c95a7a..da6e56f8ea2b 100644 --- a/packages/file_selector/file_selector_web/test/lib/main.dart +++ b/packages/file_selector/file_selector_web/test/lib/main.dart @@ -19,4 +19,3 @@ class MyAppState extends State { return Text('Testing... Look at the console output for results!'); } } - diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index 7317338cc466..47515a55a53b 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -36,7 +36,8 @@ final File textFile2 = File([bytes2], 'test2.txt'); void main() { E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; - print("Note that the following message can be ignored in the test output:\n'File chooser dialog can only be shown with a user activation'"); + print( + "Note that the following message can be ignored in the test output:\n'File chooser dialog can only be shown with a user activation'"); FileSelectorPlugin plugin; @@ -50,7 +51,7 @@ void main() { testWidgets('creates correct input element', (WidgetTester tester) async { final overrides = FileSelectorPluginTestOverrides( - getFilesWhenReady: (_) => Future.value([ XFile('path') ]), + getFilesWhenReady: (_) => Future.value([XFile('path')]), ); plugin = FileSelectorPlugin( @@ -59,25 +60,28 @@ void main() { final container = querySelector('#${domElementId}'); - final file = await plugin.loadFile(acceptedTypeGroups: [ textGroup ]); + final file = await plugin.loadFile(acceptedTypeGroups: [textGroup]); expect(file, isNotNull); - final result = container?.children?.firstWhere((element) => element.tagName == 'INPUT', orElse: () => null); + final result = container?.children?.firstWhere( + (element) => element.tagName == 'INPUT', + orElse: () => null); expect(result, isNotNull); expect(result.getAttribute('type'), 'file'); - expect(result.getAttribute('accept'), 'plain/text,application/json,.txt,.json'); + expect(result.getAttribute('accept'), + 'plain/text,application/json,.txt,.json'); }); testWidgets('input element is clicked', (WidgetTester tester) async { final mockInput = FileUploadInputElement(); final overrides = FileSelectorPluginTestOverrides( - getFilesWhenReady: (_) => Future.value([ XFile('path') ]), + getFilesWhenReady: (_) => Future.value([XFile('path')]), createFileInputElement: (_, __) => mockInput, ); - + plugin = FileSelectorPlugin( overrides: overrides, ); @@ -85,7 +89,7 @@ void main() { bool clicked = false; mockInput.onClick.listen((event) => clicked = true); - await plugin.loadFile(acceptedTypeGroups: [ textGroup ]); + await plugin.loadFile(acceptedTypeGroups: [textGroup]); expect(clicked, true); }); @@ -119,12 +123,10 @@ void main() { }); }); - - group('loadFiles(..)', () { testWidgets('creates correct input element', (WidgetTester tester) async { final overrides = FileSelectorPluginTestOverrides( - getFilesWhenReady: (_) => Future.value([ XFile('path'), XFile('path2') ]), + getFilesWhenReady: (_) => Future.value([XFile('path'), XFile('path2')]), ); plugin = FileSelectorPlugin( @@ -133,15 +135,18 @@ void main() { final container = querySelector('#${domElementId}'); - final files = await plugin.loadFiles(acceptedTypeGroups: [ textGroup ]); + final files = await plugin.loadFiles(acceptedTypeGroups: [textGroup]); expect(files, isNotNull); - final FileUploadInputElement result = container?.children?.firstWhere((element) => element.tagName == 'INPUT', orElse: () => null); + final FileUploadInputElement result = container?.children?.firstWhere( + (element) => element.tagName == 'INPUT', + orElse: () => null); expect(result, isNotNull); expect(result.getAttribute('type'), 'file'); - expect(result.getAttribute('accept'), 'plain/text,application/json,.txt,.json'); + expect(result.getAttribute('accept'), + 'plain/text,application/json,.txt,.json'); expect(result.multiple, true); }); @@ -149,12 +154,10 @@ void main() { final mockInput = FileUploadInputElement(); final overrides = FileSelectorPluginTestOverrides( - getFilesWhenReady: (_) => Future.value([ XFile('path') ]), + getFilesWhenReady: (_) => Future.value([XFile('path')]), createFileInputElement: (_, __) => mockInput, ); - - plugin = FileSelectorPlugin( overrides: overrides, ); @@ -162,7 +165,7 @@ void main() { bool clicked = false; mockInput.onClick.listen((event) => clicked = true); - await plugin.loadFiles(acceptedTypeGroups: [ textGroup ]); + await plugin.loadFiles(acceptedTypeGroups: [textGroup]); expect(clicked, true); }); @@ -196,7 +199,7 @@ void main() { final contents = await loadedFile1.readAsString(); expect(contents, expectedStringContents); expect(loadedFile1.name, textFile.name); - + final contents2 = await loadedFile2.readAsString(); expect(contents2, expectedStringContents2); expect(loadedFile2.name, textFile2.name); @@ -212,45 +215,47 @@ void main() { group('XFile saveTo(..)', () { testWidgets('creates a DOM container', (WidgetTester tester) async { XFile file = XFile.fromData(bytes); - + await file.saveTo(''); final container = querySelector('#${xFileDomElementId}'); - + expect(container, isNotNull); }); - + testWidgets('create anchor element', (WidgetTester tester) async { XFile file = XFile.fromData(bytes, name: textFile.name); - + await file.saveTo('path'); final container = querySelector('#${xFileDomElementId}'); - final AnchorElement element = container?.children?.firstWhere((element) => element.tagName == 'A', orElse: () => null); - + final AnchorElement element = container?.children + ?.firstWhere((element) => element.tagName == 'A', orElse: () => null); + expect(element, isNotNull); expect(element.href, file.path); expect(element.download, file.name); }); - + testWidgets('anchor element is clicked', (WidgetTester tester) async { final mockAnchor = AnchorElement(); - + XFileTestOverrides overrides = XFileTestOverrides( - createAnchorElement: (_,__) => mockAnchor, + createAnchorElement: (_, __) => mockAnchor, ); - - XFile file = XFile.fromData(bytes, name: textFile.name, overrides: overrides); - + + XFile file = + XFile.fromData(bytes, name: textFile.name, overrides: overrides); + bool clicked = false; mockAnchor.onClick.listen((event) => clicked = true); - + await file.saveTo('path'); - + expect(clicked, true); }); }); - print("Note that the following message can be ignored in the test output:\n'File chooser dialog can only be shown with a user activation'"); - + print( + "Note that the following message can be ignored in the test output:\n'File chooser dialog can only be shown with a user activation'"); } From 73d81a1b32936120a712bb65561a07a2487b6a64 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 13:54:59 -0700 Subject: [PATCH 111/151] Document all public members --- .../file_selector/file_selector_web/lib/file_selector_web.dart | 1 + packages/file_selector/file_selector_web/test/lib/main.dart | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 02e5a1a2e79c..93edaa2f59c5 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -222,6 +222,7 @@ class FileSelectorPluginTestOverrides { /// For overriding waiting for the files to be ready. Useful for testing so we do not hang here. Future> Function(InputElement input) getFilesWhenReady; + /// Constructor for test override class FileSelectorPluginTestOverrides( {this.createFileInputElement, this.getFilesFromInputElement, diff --git a/packages/file_selector/file_selector_web/test/lib/main.dart b/packages/file_selector/file_selector_web/test/lib/main.dart index da6e56f8ea2b..84c77ea76f01 100644 --- a/packages/file_selector/file_selector_web/test/lib/main.dart +++ b/packages/file_selector/file_selector_web/test/lib/main.dart @@ -8,11 +8,13 @@ void main() { runApp(MyApp()); } +/// An app that runs the tests class MyApp extends StatefulWidget { @override MyAppState createState() => MyAppState(); } +/// State for MyApp class MyAppState extends State { @override Widget build(BuildContext context) { From 83acae02ff76cb500a9c0612ebc01e09db6c1c57 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 15:47:52 -0700 Subject: [PATCH 112/151] Move helpers out of XFile class --- .../lib/src/types/x_file/html.dart | 49 +++------------ .../lib/src/web_helpers/web_helpers.dart | 34 ++++++++++ .../lib/file_selector_web.dart | 62 +++++++------------ 3 files changed, 64 insertions(+), 81 deletions(-) create mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/web_helpers/web_helpers.dart diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index fbbe0a139dc1..2493e9dd2a9d 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -5,6 +5,7 @@ import 'package:http/http.dart' as http show readBytes; import 'package:meta/meta.dart'; import 'dart:html'; +import '../../web_helpers/web_helpers.dart'; import './base.dart'; /// A XFile that works on web. @@ -110,50 +111,18 @@ class XFile extends XFileBase { /// For the web implementation, the path variable is ignored. void saveTo(String path) async { // Create a DOM container where we can host the anchor. - _target = _ensureInitialized('__x_file_dom_element'); + _target = ensureInitialized('__x_file_dom_element'); // Create an tag with the appropriate download attributes and click it - final AnchorElement element = createAnchorElement(this.path, this.name); + // May be overridden with XFileTestOverrides + final AnchorElement element = + (_hasTestOverrides && _overrides.createAnchorElement != null) + ? _overrides.createAnchorElement(this.path, this.name) + : createAnchorElement(this.path, this.name); - _addElementToDomAndClick(element); - } - - /// Create anchor element with download attribute - @visibleForTesting - AnchorElement createAnchorElement(String href, String suggestedName) { - if (_hasTestOverrides && _overrides.createAnchorElement != null) { - return _overrides.createAnchorElement(href, suggestedName); - } - - final element = AnchorElement(href: href); - - if (suggestedName == null) { - element.download = 'download'; - } else { - element.download = suggestedName; - } - - return element; - } - - void _addElementToDomAndClick(Element element) { - // Add the file input element and click it - // All previous elements will be removed before adding the new one + // Clear the children in our container so we can add an element to click _target.children.clear(); - _target.children.add(element); - element.click(); - } - - /// Initializes a DOM container where we can host elements. - Element _ensureInitialized(String id) { - var target = querySelector('#${id}'); - if (target == null) { - final Element targetElement = Element.tag('flt-x-file')..id = id; - - querySelector('body').children.add(targetElement); - target = targetElement; - } - return target; + addElementToContainerAndClick(_target, element); } } diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/web_helpers/web_helpers.dart b/packages/file_selector/file_selector_platform_interface/lib/src/web_helpers/web_helpers.dart new file mode 100644 index 000000000000..9e40e562bc9a --- /dev/null +++ b/packages/file_selector/file_selector_platform_interface/lib/src/web_helpers/web_helpers.dart @@ -0,0 +1,34 @@ +import 'dart:html'; + +/// Create anchor element with download attribute +AnchorElement createAnchorElement(String href, String suggestedName) { + final element = AnchorElement(href: href); + + if (suggestedName == null) { + element.download = 'download'; + } else { + element.download = suggestedName; + } + + return element; +} + +/// Add an element to a container and click it +void addElementToContainerAndClick(Element container, Element element) { + // Add the element and click it + // All previous elements will be removed before adding the new one + container.children.add(element); + element.click(); +} + +/// Initializes a DOM container where we can host elements. +Element ensureInitialized(String id) { + var target = querySelector('#${id}'); + if (target == null) { + final Element targetElement = Element.tag('flt-x-file')..id = id; + + querySelector('body').children.add(targetElement); + target = targetElement; + } + return target; +} diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 93edaa2f59c5..a378320b5e39 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:html'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:file_selector_platform_interface/src/web_helpers/web_helpers.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:meta/meta.dart'; @@ -11,6 +12,11 @@ final String _kFileSelectorInputsDomId = '__file_selector_web_file_input'; /// /// This class implements the `package:file_selector` functionality for the web. class FileSelectorPlugin extends FileSelectorPlatform { + Element _target; + final FileSelectorPluginTestOverrides _overrides; + + bool get _hasTestOverrides => _overrides != null; + /// Open file dialog for loading files and return a XFile @override Future loadFile({ @@ -47,9 +53,19 @@ class FileSelectorPlugin extends FileSelectorPlatform { }) => Future.value(); - Element _target; - final FileSelectorPluginTestOverrides _overrides; - bool get _hasTestOverrides => _overrides != null; + /// Load Helper + Future> _loadFileHelper( + bool multiple, List acceptedTypes) { + final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); + + final FileUploadInputElement element = + createFileInputElement(acceptedTypeString, multiple); + + _target.children.clear(); + addElementToContainerAndClick(_target, element); + + return getFilesWhenReady(element); + } /// Default constructor, initializes _target to a DOM element that we can use /// to host HTML elements. @@ -57,7 +73,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { FileSelectorPlugin({ @visibleForTesting FileSelectorPluginTestOverrides overrides, }) : _overrides = overrides { - _target = _ensureInitialized(_kFileSelectorInputsDomId); + _target = ensureInitialized(_kFileSelectorInputsDomId); } /// Registers this class as the default instance of [FileSelectorPlatform]. @@ -74,7 +90,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { } } - /// Convert list of filter groups to a comma-separated string + /// Convert list of XTypeGroups to a comma-separated string String _getStringFromFilterGroup(List acceptedTypes) { List allTypes = List(); for (XTypeGroup group in acceptedTypes ?? []) { @@ -115,14 +131,6 @@ class FileSelectorPlugin extends FileSelectorPlatform { return element; } - void _addElementToDomAndClick(Element element) { - // Add the file input element and click it - // All previous elements will be removed before adding the new one - _target.children.clear(); - _target.children.add(element); - element.click(); - } - List _getXFilesFromFiles(List files) { List xFiles = List(); @@ -180,34 +188,6 @@ class FileSelectorPlugin extends FileSelectorPlatform { return _completer.future; } - - /// Initializes a DOM container where we can host elements. - Element _ensureInitialized(String id) { - var target = querySelector('#${id}'); - if (target == null) { - final Element targetElement = Element.tag('flt-file-picker-inputs') - ..id = id; - - querySelector('body').children.add(targetElement); - target = targetElement; - } - return target; - } - - /// NEW API - - /// Load Helper - Future> _loadFileHelper( - bool multiple, List acceptedTypes) { - final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); - - final FileUploadInputElement element = - createFileInputElement(acceptedTypeString, multiple); - - _addElementToDomAndClick(element); - - return getFilesWhenReady(element); - } } /// Overrides some functions to allow testing From 70cc82592dc10b967751bba9d9e864a43fd5bd9c Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 16:26:40 -0700 Subject: [PATCH 113/151] Use simpiler structures in method channels Also update web implementations to async --- .../method_channel_file_selector.dart | 16 +++++++++++----- .../file_selector_web/lib/file_selector_web.dart | 16 ++++------------ 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index 097075c96287..d59160846129 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -17,14 +17,15 @@ class MethodChannelFileSelector extends FileSelectorPlatform { List acceptedTypeGroups, String initialDirectory, String confirmButtonText, - }) { - return _channel.invokeMethod( + }) async { + String path = await _channel.invokeMethod( 'loadFile', { 'acceptedTypes': acceptedTypeGroups, 'initialDirectory': initialDirectory, }, ); + return XFile(path); } /// Load multiple files from user's computer and return it as an XFile @@ -33,14 +34,19 @@ class MethodChannelFileSelector extends FileSelectorPlatform { List acceptedTypeGroups, String initialDirectory, String confirmButtonText, - }) { - return _channel.invokeMethod>( + }) async { + final pathList = await _channel.invokeMethod>( 'loadFiles', { 'acceptedTypes': acceptedTypeGroups, 'initialDirectory': initialDirectory, }, ); + List fileList = List(); + for (String path in pathList) { + fileList.add(XFile(path)); + } + return fileList; } /// Saves the file to user's Disk @@ -51,7 +57,7 @@ class MethodChannelFileSelector extends FileSelectorPlatform { String suggestedName, String confirmButtonText, }) async { - return _channel.invokeMethod( + return _channel.invokeMethod( 'saveFile', { 'initialDirectory': initialDirectory, diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index a378320b5e39..8f964736a28a 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -23,15 +23,8 @@ class FileSelectorPlugin extends FileSelectorPlatform { List acceptedTypeGroups, String initialDirectory, String confirmButtonText, - }) { - Completer _completer = Completer(); - _loadFileHelper(false, acceptedTypeGroups).then((list) { - _completer.complete(list.first); - }).catchError((err) { - _completer.completeError(err); - }); - - return _completer.future; + }) async { + return (await _loadFileHelper(false, acceptedTypeGroups)).first; } /// Open file dialog for loading files and return a XFile @@ -40,7 +33,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { List acceptedTypeGroups, String initialDirectory, String confirmButtonText, - }) { + }) async { return _loadFileHelper(true, acceptedTypeGroups); } @@ -50,8 +43,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { String initialDirectory, String suggestedName, String confirmButtonText, - }) => - Future.value(); + }) async => null; /// Load Helper Future> _loadFileHelper( From 415706c9ece9b4ad9010fa34fe2a503fe04fbf52 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 16:40:42 -0700 Subject: [PATCH 114/151] Change some variables to final --- .../file_selector/example/lib/main.dart | 27 +++++++------------ .../test/file_selector_test.dart | 23 +++++++--------- .../lib/src/types/x_file/html.dart | 7 +---- .../lib/file_selector_web.dart | 5 ++-- 4 files changed, 24 insertions(+), 38 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 06ae8125e4d6..8fbd2a0c04bd 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -46,19 +46,14 @@ class _SaveTestState extends State { } void _saveFile() async { - String path = await getSavePath(); + final path = await getSavePath(); - Uint8List data; - data = Uint8List.fromList(_fileController.text.codeUnits); + final data = Uint8List.fromList(_fileController.text.codeUnits); - XFile new_file; - - if (_nameController.text == '') { - new_file = XFile.fromData(data, mimeType: 'text/plain'); - } else { - new_file = XFile.fromData(data, - mimeType: 'text/plain', name: _nameController.text); - } + final new_file = (_nameController.text == '') + ? XFile.fromData(data, mimeType: 'text/plain') + : XFile.fromData(data, + mimeType: 'text/plain', name: _nameController.text); new_file.saveTo(path); } @@ -118,10 +113,9 @@ class LoadTest extends StatefulWidget { class _LoadTestState extends State { void _onLoadImageFile() async { - XTypeGroup typeGroup = - XTypeGroup(label: 'images', extensions: ['jpg', 'png']); + final typeGroup = XTypeGroup(label: 'images', extensions: ['jpg', 'png']); - XFile file = await loadFile(acceptedTypeGroups: [typeGroup]); + final file = await loadFile(acceptedTypeGroups: [typeGroup]); await showDialog( context: context, @@ -131,10 +125,9 @@ class _LoadTestState extends State { } void _onLoadTextFile() async { - XTypeGroup typeGroup = - XTypeGroup(label: 'images', extensions: ['txt', 'json']); + final typeGroup = XTypeGroup(label: 'images', extensions: ['txt', 'json']); - XFile file = await loadFile(acceptedTypeGroups: [typeGroup]); + final file = await loadFile(acceptedTypeGroups: [typeGroup]); await showDialog( context: context, diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index 81b1772fb3bc..2640e91ebfa2 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -9,14 +9,13 @@ import 'package:file_selector/file_selector.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; void main() { - final MockFileSelector mock = MockFileSelector(); + final mock = MockFileSelector(); FileSelectorPlatform.instance = mock; test('getSavePath', () async { - String expectedPath = '/example/path'; + final expectedPath = '/example/path'; - XTypeGroup typeGroup = - XTypeGroup(label: 'group', extensions: ['jpg', 'png']); + final typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); when(mock.getSavePath( acceptedTypeGroups: [typeGroup], @@ -25,7 +24,7 @@ void main() { confirmButtonText: 'save', )).thenAnswer((_) => Future.value(expectedPath)); - String result = await getSavePath( + final result = await getSavePath( acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', suggestedName: 'name', @@ -35,10 +34,9 @@ void main() { }); test('loadFile', () async { - XFile file = XFile('path'); + final file = XFile('path'); - XTypeGroup typeGroup = - XTypeGroup(label: 'group', extensions: ['jpg', 'png']); + final typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); when(mock.loadFile( acceptedTypeGroups: [typeGroup], @@ -46,7 +44,7 @@ void main() { confirmButtonText: 'load', )).thenAnswer((_) => Future.value(file)); - XFile result = await loadFile( + final result = await loadFile( acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', confirmButtonText: 'load'); @@ -55,10 +53,9 @@ void main() { }); test('loadFiles', () async { - XFile file = XFile('path'); + final file = XFile('path'); - XTypeGroup typeGroup = - XTypeGroup(label: 'group', extensions: ['jpg', 'png']); + final typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); when(mock.loadFiles( acceptedTypeGroups: [typeGroup], @@ -66,7 +63,7 @@ void main() { confirmButtonText: 'load', )).thenAnswer((_) => Future.value([file])); - List result = await loadFiles( + final result = await loadFiles( acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', confirmButtonText: 'load'); diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart index 2493e9dd2a9d..fe898eb4ca62 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart @@ -61,12 +61,7 @@ class XFile extends XFileBase { _lastModified = lastModified, super(path) { if (path == null) { - Blob blob; - if (mimeType == null) { - blob = Blob([bytes]); - } else { - blob = Blob([bytes], mimeType); - } + final blob = (mimeType == null) ? Blob([bytes]) : Blob([bytes], mimeType); this.path = Url.createObjectUrl(blob); } } diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 8f964736a28a..29f3813adfcc 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -43,7 +43,8 @@ class FileSelectorPlugin extends FileSelectorPlatform { String initialDirectory, String suggestedName, String confirmButtonText, - }) async => null; + }) async => + null; /// Load Helper Future> _loadFileHelper( @@ -126,7 +127,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { List _getXFilesFromFiles(List files) { List xFiles = List(); - Duration timeZoneOffset = DateTime.now().timeZoneOffset; + final timeZoneOffset = DateTime.now().timeZoneOffset; for (File file in files) { String url = Url.createObjectUrl(file); From 08d86e9fab4bdc45e89f16aaea320953d7b444da Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 16:52:54 -0700 Subject: [PATCH 115/151] Assert in constructor initializer list instead of body --- .../lib/src/types/x_type_group/x_type_group.dart | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart index 8b02af8ea24e..b4223c8ca77c 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart @@ -23,15 +23,12 @@ class XTypeGroup { this.mimeTypes, this.macUTIs, this.webWildCards, - }) { - if (this.extensions == null && - this.mimeTypes == null && - this.macUTIs == null && - this.webWildCards == null) { - throw ArgumentError( - "At least one type must be provided for an XTypeGroup."); - } - } + }) : assert( + !(extensions == null && + mimeTypes == null && + macUTIs == null && + webWildCards == null), + "At least one type must be provided for an XTypeGroup."); /// The 'name' or reference to this group of types final String label; From 8a30f6d988a35dfe8f8839832193f6dd1d0bab71 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Fri, 28 Aug 2020 17:25:17 -0700 Subject: [PATCH 116/151] Change Error to PlatformException --- .../file_selector_web/lib/file_selector_web.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 29f3813adfcc..71a5a6bc169c 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -3,6 +3,7 @@ import 'dart:html'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:file_selector_platform_interface/src/web_helpers/web_helpers.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:meta/meta.dart'; @@ -174,9 +175,10 @@ class FileSelectorPlugin extends FileSelectorPlatform { }); element.onError.first.then((event) { - if (!_completer.isCompleted) { - _completer.completeError(event); - } + throw PlatformException( + code: 'file_input', + message: "Input element failed on event with target: " + + event?.target?.toString()); }); return _completer.future; From b855b1c2038806d6a4e69853022fa1f538c0ceb3 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Mon, 31 Aug 2020 10:38:54 -0700 Subject: [PATCH 117/151] Update README.md --- packages/file_selector/file_selector/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index 670bdf2012a1..db0f152bc5e2 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -1,3 +1,3 @@ -# file_picker +# file_selector -A Flutter plugin that currently doesn't do anything. +A Flutter plugin that manages files and interactions with file dialogs. From ade07e4bfb2e58e0c154f7f83879906eb31348ca Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 1 Sep 2020 15:57:02 -0700 Subject: [PATCH 118/151] Address Some Comments Improvements to code with cleaner dart methods Semantic fixes Reorganize method channels --- .../method_channel_file_selector.dart | 11 ++--- .../file_selector_interface.dart | 4 +- .../lib/file_selector_web.dart | 43 ++++++------------- .../test/test_driver/web_e2e.dart | 6 ++- 4 files changed, 24 insertions(+), 40 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index d59160846129..057697fb1dcd 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -4,7 +4,6 @@ import 'package:flutter/services.dart'; -import '../platform_interface/file_selector_interface.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); @@ -19,10 +18,11 @@ class MethodChannelFileSelector extends FileSelectorPlatform { String confirmButtonText, }) async { String path = await _channel.invokeMethod( - 'loadFile', + 'loadFiles', { 'acceptedTypes': acceptedTypeGroups, 'initialDirectory': initialDirectory, + 'multiple': false, }, ); return XFile(path); @@ -40,13 +40,10 @@ class MethodChannelFileSelector extends FileSelectorPlatform { { 'acceptedTypes': acceptedTypeGroups, 'initialDirectory': initialDirectory, + 'multiple': true, }, ); - List fileList = List(); - for (String path in pathList) { - fileList.add(XFile(path)); - } - return fileList; + return pathList.map((path) => XFile(path)).toList(); } /// Saves the file to user's Disk diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index 66b14c75efd8..e675f0128028 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -51,7 +51,7 @@ abstract class FileSelectorPlatform extends PlatformInterface { String initialDirectory, String confirmButtonText, }) { - throw UnimplementedError('loadFile() has not been implemented.'); + throw UnimplementedError('loadFiles() has not been implemented.'); } /// Open file dialog for saving files and return a file path at which to save @@ -61,6 +61,6 @@ abstract class FileSelectorPlatform extends PlatformInterface { String suggestedName, String confirmButtonText, }) { - throw UnimplementedError('saveFile() has not been implemented.'); + throw UnimplementedError('getSavePath() has not been implemented.'); } } diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 71a5a6bc169c..c52a6e3f7985 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -75,35 +75,21 @@ class FileSelectorPlugin extends FileSelectorPlatform { FileSelectorPlatform.instance = FileSelectorPlugin(); } - void _verifyXTypeGroup(XTypeGroup group) { - if (group.extensions == null && - group.mimeTypes == null && - group.webWildCards == null) { - StateError( - "This XTypeGroup does not have types supported by the web implementation of loadFile."); - } - } - /// Convert list of XTypeGroups to a comma-separated string String _getStringFromFilterGroup(List acceptedTypes) { List allTypes = List(); + for (XTypeGroup group in acceptedTypes ?? []) { - _verifyXTypeGroup(group); - - for (String mimeType in group.mimeTypes ?? []) { - allTypes.add(mimeType); - } - for (String extension in group.extensions ?? []) { - String ext = extension; - if (ext.isNotEmpty && ext[0] != '.') { - ext = '.' + ext; - } - - allTypes.add(ext); - } - for (String webWildCard in group.webWildCards ?? []) { - allTypes.add(webWildCard); - } + assert( + !((group.extensions == null || group.extensions.isEmpty) && + (group.mimeTypes == null || group.mimeTypes.isEmpty) && + (group.webWildCards == null || group.webWildCards.isEmpty)), + 'At least one of extensions / mimeTypes / webWildCards is required for web.'); + + allTypes.addAll(group.extensions + .map((ext) => ext.isNotEmpty && ext[0] != '.' ? '.' + ext : ext)); + allTypes.addAll(group.mimeTypes ?? []); + allTypes.addAll(group.webWildCards ?? []); } return allTypes?.where((e) => e.isNotEmpty)?.join(',') ?? ''; } @@ -175,10 +161,9 @@ class FileSelectorPlugin extends FileSelectorPlatform { }); element.onError.first.then((event) { - throw PlatformException( - code: 'file_input', - message: "Input element failed on event with target: " + - event?.target?.toString()); + ErrorEvent error = event; + _completer.completeError( + PlatformException(code: error.type, message: error.message)); }); return _completer.future; diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index 47515a55a53b..01044c0acce8 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -71,7 +71,7 @@ void main() { expect(result, isNotNull); expect(result.getAttribute('type'), 'file'); expect(result.getAttribute('accept'), - 'plain/text,application/json,.txt,.json'); + '.txt,.json,plain/text,application/json'); }); testWidgets('input element is clicked', (WidgetTester tester) async { @@ -143,10 +143,12 @@ void main() { (element) => element.tagName == 'INPUT', orElse: () => null); + print (result.getAttribute('accept')); + expect(result, isNotNull); expect(result.getAttribute('type'), 'file'); expect(result.getAttribute('accept'), - 'plain/text,application/json,.txt,.json'); + '.txt,.json,plain/text,application/json'); expect(result.multiple, true); }); From 438831df401001ba18d231a994b91830c1a95e21 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 1 Sep 2020 16:56:28 -0700 Subject: [PATCH 119/151] Move e2e tests to integration_test --- packages/file_selector/file_selector_web/pubspec.yaml | 3 ++- packages/file_selector/file_selector_web/test/pubspec.yaml | 3 ++- .../file_selector_web/test/test_driver/web_e2e.dart | 4 ++-- .../file_selector_web/test/test_driver/web_e2e_test.dart | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 033bef65a459..742065c4132a 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -26,9 +26,10 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - url_launcher: ^5.2.5 pedantic: ^1.8.0 mockito: ^4.1.1 + integration_test: + path: ../../integration_test environment: sdk: ">=2.2.0 <3.0.0" diff --git a/packages/file_selector/file_selector_web/test/pubspec.yaml b/packages/file_selector/file_selector_web/test/pubspec.yaml index 12bbc2ac8720..f5e42e327564 100644 --- a/packages/file_selector/file_selector_web/test/pubspec.yaml +++ b/packages/file_selector/file_selector_web/test/pubspec.yaml @@ -18,6 +18,7 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter - e2e: ^0.6.1 + integration_test: + path: ../../../integration_test http: ^0.12.2 mockito: ^4.1.1 \ No newline at end of file diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart index 01044c0acce8..03f2019955df 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:e2e/e2e.dart'; +import 'package:integration_test/integration_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'dart:convert'; @@ -34,7 +34,7 @@ final File textFile2 = File([bytes2], 'test2.txt'); /// Test Markers void main() { - E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); print( "Note that the following message can be ignored in the test output:\n'File chooser dialog can only be shown with a user activation'"); diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e_test.dart b/packages/file_selector/file_selector_web/test/test_driver/web_e2e_test.dart index a29203f7dcdd..f26b6a310cfe 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_e2e_test.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_e2e_test.dart @@ -2,6 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:e2e/e2e_driver.dart' as e2e; +import 'package:integration_test/integration_test_driver.dart'; -Future main() async => e2e.main(); +Future main() async => integrationDriver(); From 7daf109aea9b1cf82f9f09b335648727a2ce6bee Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 1 Sep 2020 17:00:02 -0700 Subject: [PATCH 120/151] Rename e2e files and update test/README --- packages/file_selector/file_selector_web/test/README.md | 5 +---- .../test/test_driver/{web_e2e.dart => web_integration.dart} | 0 .../{web_e2e_test.dart => web_integration_test.dart} | 0 3 files changed, 1 insertion(+), 4 deletions(-) rename packages/file_selector/file_selector_web/test/test_driver/{web_e2e.dart => web_integration.dart} (100%) rename packages/file_selector/file_selector_web/test/test_driver/{web_e2e_test.dart => web_integration_test.dart} (100%) diff --git a/packages/file_selector/file_selector_web/test/README.md b/packages/file_selector/file_selector_web/test/README.md index f623e9fd08d3..0a3f392fe3e4 100644 --- a/packages/file_selector/file_selector_web/test/README.md +++ b/packages/file_selector/file_selector_web/test/README.md @@ -11,7 +11,4 @@ Make sure you have updated to the latest Flutter master. 4. Change into the `test` directory of your clone. -5. Run tests: `flutter drive -d web-server --release --browser-name=chrome --target=test_driver/TEST_NAME_e2e.dart`, or (in Linux): - - * Single: `./run_test test_driver/TEST_NAME_e2e.dart` - * All: `./run_test` +5. Run tests: `flutter drive -d web-server --release --browser-name=chrome --target=test_driver/TEST_NAME_integration.dart`. diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart b/packages/file_selector/file_selector_web/test/test_driver/web_integration.dart similarity index 100% rename from packages/file_selector/file_selector_web/test/test_driver/web_e2e.dart rename to packages/file_selector/file_selector_web/test/test_driver/web_integration.dart diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_e2e_test.dart b/packages/file_selector/file_selector_web/test/test_driver/web_integration_test.dart similarity index 100% rename from packages/file_selector/file_selector_web/test/test_driver/web_e2e_test.dart rename to packages/file_selector/file_selector_web/test/test_driver/web_integration_test.dart From 67005aa2f68333c58c171999fd804f2db0fde062 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 1 Sep 2020 17:21:28 -0700 Subject: [PATCH 121/151] Check for empty XTypeGroup and use lastModified instead of lastModifiedDate --- .../lib/src/types/x_type_group/x_type_group.dart | 8 ++++---- .../file_selector_web/lib/file_selector_web.dart | 10 +++++----- .../test/test_driver/web_integration.dart | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart index b4223c8ca77c..58126cdbd5c2 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart @@ -24,10 +24,10 @@ class XTypeGroup { this.macUTIs, this.webWildCards, }) : assert( - !(extensions == null && - mimeTypes == null && - macUTIs == null && - webWildCards == null), + !((extensions == null || extensions.isEmpty) && + (mimeTypes == null || mimeTypes.isEmpty) && + (macUTIs == null || macUTIs.isEmpty) && + (webWildCards == null || webWildCards.isEmpty)), "At least one type must be provided for an XTypeGroup."); /// The 'name' or reference to this group of types diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index c52a6e3f7985..d03f1494277d 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -114,16 +114,16 @@ class FileSelectorPlugin extends FileSelectorPlatform { List _getXFilesFromFiles(List files) { List xFiles = List(); - final timeZoneOffset = DateTime.now().timeZoneOffset; - for (File file in files) { String url = Url.createObjectUrl(file); String name = file.name; int length = file.size; - DateTime modified = file.lastModifiedDate.add(timeZoneOffset); + int modified = file.lastModified; + + DateTime modifiedDate = DateTime.fromMillisecondsSinceEpoch(modified); - xFiles - .add(XFile(url, name: name, lastModified: modified, length: length)); + xFiles.add( + XFile(url, name: name, lastModified: modifiedDate, length: length)); } return xFiles; diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_integration.dart b/packages/file_selector/file_selector_web/test/test_driver/web_integration.dart index 03f2019955df..acf46a6bfbbc 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_integration.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_integration.dart @@ -143,7 +143,7 @@ void main() { (element) => element.tagName == 'INPUT', orElse: () => null); - print (result.getAttribute('accept')); + print(result.getAttribute('accept')); expect(result, isNotNull); expect(result.getAttribute('type'), 'file'); From 50c4ea2b7bab4b170fc71b4704307ee2ad7ef71c Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 1 Sep 2020 17:42:01 -0700 Subject: [PATCH 122/151] Change load to open --- .../file_selector/example/lib/main.dart | 36 +++++++++---------- .../file_selector/lib/file_selector.dart | 8 ++--- .../test/file_selector_test.dart | 12 +++---- .../method_channel_file_selector.dart | 8 ++--- .../file_selector_interface.dart | 8 ++--- .../lib/file_selector_web.dart | 10 +++--- .../test/test_driver/web_integration.dart | 16 ++++----- 7 files changed, 49 insertions(+), 49 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 8fbd2a0c04bd..0a383ded9728 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -20,7 +20,7 @@ class MyApp extends StatelessWidget { home: MyHomePage(title: 'File Selector Demo Home Page'), routes: { '/save': (context) => SaveTest(), - '/load': (context) => LoadTest(), + '/open': (context) => OpenTest(), }, ); } @@ -102,20 +102,20 @@ class _SaveTestState extends State { } } -/// Screen that shows an example of loadFile(s) -class LoadTest extends StatefulWidget { +/// Screen that shows an example of openFile(s) +class OpenTest extends StatefulWidget { /// Default constructor - LoadTest({Key key}) : super(key: key); + OpenTest({Key key}) : super(key: key); @override - _LoadTestState createState() => _LoadTestState(); + _OpenTestState createState() => _OpenTestState(); } -class _LoadTestState extends State { - void _onLoadImageFile() async { +class _OpenTestState extends State { + void _onOpenImageFile() async { final typeGroup = XTypeGroup(label: 'images', extensions: ['jpg', 'png']); - final file = await loadFile(acceptedTypeGroups: [typeGroup]); + final file = await openFile(acceptedTypeGroups: [typeGroup]); await showDialog( context: context, @@ -124,10 +124,10 @@ class _LoadTestState extends State { }); } - void _onLoadTextFile() async { + void _onOpenTextFile() async { final typeGroup = XTypeGroup(label: 'images', extensions: ['txt', 'json']); - final file = await loadFile(acceptedTypeGroups: [typeGroup]); + final file = await openFile(acceptedTypeGroups: [typeGroup]); await showDialog( context: context, @@ -140,19 +140,19 @@ class _LoadTestState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Load Example"), + title: Text("Open Example"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ RaisedButton( - child: Text('Press to load an image file(png, jpg)'), - onPressed: () => _onLoadImageFile(), + child: Text('Press to open an image file(png, jpg)'), + onPressed: () => _onOpenImageFile(), ), RaisedButton( - child: Text('Press to load a text file (json, txt)'), - onPressed: () => _onLoadTextFile(), + child: Text('Press to open a text file (json, txt)'), + onPressed: () => _onOpenTextFile(), ), ], ), @@ -195,7 +195,7 @@ class _TextDisplayState extends State { child: SingleChildScrollView( child: Text( fileContents ?? - 'Loading file contents...\nThis may take a while if your file is large.', + 'Opening file contents...\nThis may take a while if your file is large.', ), ), ), @@ -276,8 +276,8 @@ class _MyHomePageState extends State { onPressed: () => Navigator.pushNamed(context, '/save'), ), RaisedButton( - child: Text('Press to try loading a file'), - onPressed: () => Navigator.pushNamed(context, '/load'), + child: Text('Press to try opening a file'), + onPressed: () => Navigator.pushNamed(context, '/open'), ), ], ), diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index 659b4e87fe4f..f9dc71cd3f00 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -10,24 +10,24 @@ export 'package:file_selector_platform_interface/file_selector_platform_interfac show XFile, XTypeGroup; /// Open file dialog for loading files and return a file path -Future loadFile({ +Future openFile({ List acceptedTypeGroups, String initialDirectory, String confirmButtonText, }) { - return FileSelectorPlatform.instance.loadFile( + return FileSelectorPlatform.instance.openFile( acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory, confirmButtonText: confirmButtonText); } /// Open file dialog for loading files and return a list of file paths -Future> loadFiles({ +Future> openFiles({ List acceptedTypeGroups, String initialDirectory, String confirmButtonText, }) { - return FileSelectorPlatform.instance.loadFiles( + return FileSelectorPlatform.instance.openFiles( acceptedTypeGroups: acceptedTypeGroups, initialDirectory: initialDirectory, confirmButtonText: confirmButtonText); diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index 2640e91ebfa2..ad0239eec8f8 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -33,18 +33,18 @@ void main() { expect(result, expectedPath); }); - test('loadFile', () async { + test('openFile', () async { final file = XFile('path'); final typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); - when(mock.loadFile( + when(mock.openFile( acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', confirmButtonText: 'load', )).thenAnswer((_) => Future.value(file)); - final result = await loadFile( + final result = await openFile( acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', confirmButtonText: 'load'); @@ -52,18 +52,18 @@ void main() { expect(result, isNotNull); }); - test('loadFiles', () async { + test('openFiles', () async { final file = XFile('path'); final typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); - when(mock.loadFiles( + when(mock.openFiles( acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', confirmButtonText: 'load', )).thenAnswer((_) => Future.value([file])); - final result = await loadFiles( + final result = await openFiles( acceptedTypeGroups: [typeGroup], initialDirectory: 'dir', confirmButtonText: 'load'); diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index 057697fb1dcd..1a6ec05816a0 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -12,13 +12,13 @@ const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); class MethodChannelFileSelector extends FileSelectorPlatform { /// Load a file from user's computer and return it as an XFile @override - Future loadFile({ + Future openFile({ List acceptedTypeGroups, String initialDirectory, String confirmButtonText, }) async { String path = await _channel.invokeMethod( - 'loadFiles', + 'openFiles', { 'acceptedTypes': acceptedTypeGroups, 'initialDirectory': initialDirectory, @@ -30,13 +30,13 @@ class MethodChannelFileSelector extends FileSelectorPlatform { /// Load multiple files from user's computer and return it as an XFile @override - Future> loadFiles({ + Future> openFiles({ List acceptedTypeGroups, String initialDirectory, String confirmButtonText, }) async { final pathList = await _channel.invokeMethod>( - 'loadFiles', + 'openFiles', { 'acceptedTypes': acceptedTypeGroups, 'initialDirectory': initialDirectory, diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index e675f0128028..1990972f2e22 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -37,21 +37,21 @@ abstract class FileSelectorPlatform extends PlatformInterface { } /// Open file dialog for loading files and return a file path - Future loadFile({ + Future openFile({ List acceptedTypeGroups, String initialDirectory, String confirmButtonText, }) { - throw UnimplementedError('loadFile() has not been implemented.'); + throw UnimplementedError('openFile() has not been implemented.'); } /// Open file dialog for loading files and return a list of file paths - Future> loadFiles({ + Future> openFiles({ List acceptedTypeGroups, String initialDirectory, String confirmButtonText, }) { - throw UnimplementedError('loadFiles() has not been implemented.'); + throw UnimplementedError('openFiles() has not been implemented.'); } /// Open file dialog for saving files and return a file path at which to save diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index d03f1494277d..9bc45bba98cb 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -20,22 +20,22 @@ class FileSelectorPlugin extends FileSelectorPlatform { /// Open file dialog for loading files and return a XFile @override - Future loadFile({ + Future openFile({ List acceptedTypeGroups, String initialDirectory, String confirmButtonText, }) async { - return (await _loadFileHelper(false, acceptedTypeGroups)).first; + return (await _openFileHelper(false, acceptedTypeGroups)).first; } /// Open file dialog for loading files and return a XFile @override - Future> loadFiles({ + Future> openFiles({ List acceptedTypeGroups, String initialDirectory, String confirmButtonText, }) async { - return _loadFileHelper(true, acceptedTypeGroups); + return _openFileHelper(true, acceptedTypeGroups); } @override @@ -48,7 +48,7 @@ class FileSelectorPlugin extends FileSelectorPlatform { null; /// Load Helper - Future> _loadFileHelper( + Future> _openFileHelper( bool multiple, List acceptedTypes) { final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_integration.dart b/packages/file_selector/file_selector_web/test/test_driver/web_integration.dart index acf46a6bfbbc..bcbb404b420b 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_integration.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_integration.dart @@ -41,7 +41,7 @@ void main() { FileSelectorPlugin plugin; - group('loadFile(..)', () { + group('openFile(..)', () { testWidgets('creates a DOM container', (WidgetTester tester) async { plugin = FileSelectorPlugin(); @@ -60,7 +60,7 @@ void main() { final container = querySelector('#${domElementId}'); - final file = await plugin.loadFile(acceptedTypeGroups: [textGroup]); + final file = await plugin.openFile(acceptedTypeGroups: [textGroup]); expect(file, isNotNull); @@ -89,7 +89,7 @@ void main() { bool clicked = false; mockInput.onClick.listen((event) => clicked = true); - await plugin.loadFile(acceptedTypeGroups: [textGroup]); + await plugin.openFile(acceptedTypeGroups: [textGroup]); expect(clicked, true); }); @@ -107,7 +107,7 @@ void main() { ); // Call load file - final file = plugin.loadFile(); + final file = plugin.openFile(); // Mock selection of a file mockInput.dispatchEvent(Event('change')); @@ -123,7 +123,7 @@ void main() { }); }); - group('loadFiles(..)', () { + group('openFiles(..)', () { testWidgets('creates correct input element', (WidgetTester tester) async { final overrides = FileSelectorPluginTestOverrides( getFilesWhenReady: (_) => Future.value([XFile('path'), XFile('path2')]), @@ -135,7 +135,7 @@ void main() { final container = querySelector('#${domElementId}'); - final files = await plugin.loadFiles(acceptedTypeGroups: [textGroup]); + final files = await plugin.openFiles(acceptedTypeGroups: [textGroup]); expect(files, isNotNull); @@ -167,7 +167,7 @@ void main() { bool clicked = false; mockInput.onClick.listen((event) => clicked = true); - await plugin.loadFiles(acceptedTypeGroups: [textGroup]); + await plugin.openFiles(acceptedTypeGroups: [textGroup]); expect(clicked, true); }); @@ -185,7 +185,7 @@ void main() { ); // Call load file - final files = plugin.loadFiles(); + final files = plugin.openFiles(); // Mock selection of files mockInput.dispatchEvent(Event('change')); From 67bd59c369ed87dbbc900391fa6a748fb649fb38 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 1 Sep 2020 17:55:40 -0700 Subject: [PATCH 123/151] Add getDirectoryPath --- .../file_selector/lib/file_selector.dart | 9 +++++++++ .../file_selector/test/file_selector_test.dart | 14 ++++++++++++++ .../file_selector_interface.dart | 8 ++++++++ .../file_selector_web/lib/file_selector_web.dart | 7 +++++++ .../test/test_driver/web_integration.dart | 6 ++++++ 5 files changed, 44 insertions(+) diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index f9dc71cd3f00..54c012f57a4a 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -46,3 +46,12 @@ Future getSavePath({ suggestedName: suggestedName, confirmButtonText: confirmButtonText); } + +/// Gets a directory path from a user's file system +Future getDirectoryPath({ + String initialDirectory, + String confirmButtonText, +}) async { + return FileSelectorPlatform.instance.getDirectoryPath( + initialDirectory: initialDirectory, confirmButtonText: confirmButtonText); +} diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index ad0239eec8f8..024c8c8d314f 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -33,6 +33,20 @@ void main() { expect(result, expectedPath); }); + test('getDirectoryPath', () async { + final expectedPath = '/example/path'; + + when(mock.getDirectoryPath( + initialDirectory: 'dir', + confirmButtonText: 'save', + )).thenAnswer((_) => Future.value(expectedPath)); + + final result = await getDirectoryPath( + initialDirectory: 'dir', confirmButtonText: 'save'); + + expect(result, expectedPath); + }); + test('openFile', () async { final file = XFile('path'); diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index 1990972f2e22..53138122fcb5 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -63,4 +63,12 @@ abstract class FileSelectorPlatform extends PlatformInterface { }) { throw UnimplementedError('getSavePath() has not been implemented.'); } + + /// Open file dialog for loading directories and return a directory path + Future getDirectoryPath({ + String initialDirectory, + String confirmButtonText, + }) { + throw UnimplementedError('getDirectoryPath() has not been implemented.'); + } } diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 9bc45bba98cb..23845f4c2bfe 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -47,6 +47,13 @@ class FileSelectorPlugin extends FileSelectorPlatform { }) async => null; + @override + Future getDirectoryPath({ + String initialDirectory, + String confirmButtonText, + }) async => + null; + /// Load Helper Future> _openFileHelper( bool multiple, List acceptedTypes) { diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_integration.dart b/packages/file_selector/file_selector_web/test/test_driver/web_integration.dart index bcbb404b420b..4b44f09adf30 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_integration.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_integration.dart @@ -214,6 +214,12 @@ void main() { expect(path, completes); }); + testWidgets('getDirectoryPath completes', (WidgetTester tester) async { + plugin = FileSelectorPlugin(); + final path = plugin.getDirectoryPath(); + expect(path, completes); + }); + group('XFile saveTo(..)', () { testWidgets('creates a DOM container', (WidgetTester tester) async { XFile file = XFile.fromData(bytes); From 19ca49c15fef45ee6e50e047596e75572a9dc289 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 2 Sep 2020 12:10:10 -0700 Subject: [PATCH 124/151] Some files still referred to file_picker --- .../file_selector_platform_interface/LICENSE | 2 +- .../file_selector_platform_interface/README.md | 18 +++++++++--------- ...file_selector_platform_interface_test.dart} | 0 3 files changed, 10 insertions(+), 10 deletions(-) rename packages/file_selector/file_selector_platform_interface/test/{file_picker_platform_interface_test.dart => file_selector_platform_interface_test.dart} (100%) diff --git a/packages/file_selector/file_selector_platform_interface/LICENSE b/packages/file_selector/file_selector_platform_interface/LICENSE index c89293372cf3..0c91662b3f2f 100644 --- a/packages/file_selector/file_selector_platform_interface/LICENSE +++ b/packages/file_selector/file_selector_platform_interface/LICENSE @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2020 The Chromium Authors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are diff --git a/packages/file_selector/file_selector_platform_interface/README.md b/packages/file_selector/file_selector_platform_interface/README.md index 498936296468..d750461f2133 100644 --- a/packages/file_selector/file_selector_platform_interface/README.md +++ b/packages/file_selector/file_selector_platform_interface/README.md @@ -1,18 +1,18 @@ -# file_picker_platform_interface +# file_selector_platform_interface -A common platform interface for the `file_picker` plugin. +A common platform interface for the `file_selector` plugin. -This interface allows platform-specific implementations of the `file_picker` +This interface allows platform-specific implementations of the `file_selector` plugin, as well as the plugin itself, to ensure they are supporting the same interface. # Usage -To implement a new platform-specific implementation of `file_picker`, extend -[`FilePickerPlatform`][2] with an implementation that performs the +To implement a new platform-specific implementation of `file_selector`, extend +[`FileSelectorPlatform`][2] with an implementation that performs the platform-specific behavior, and when you register your plugin, set the default -`FilePickerPlatform` by calling -`FilePickerPlatform.instance = MyPlatformFilePicker()`. +`FileSelectorPlatform` by calling +`FileSelectorPlatform.instance = MyPlatformFileSelector()`. # Note on breaking changes @@ -22,5 +22,5 @@ over breaking changes for this package. See https://flutter.dev/go/platform-interface-breaking-changes for a discussion on why a less-clean interface is preferable to a breaking change. -[1]: ../file_picker -[2]: lib/file_picker_platform_interface.dart +[1]: ../file_selector +[2]: lib/file_selector_platform_interface.dart diff --git a/packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart b/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart similarity index 100% rename from packages/file_selector/file_selector_platform_interface/test/file_picker_platform_interface_test.dart rename to packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart From f69442668878d2393da661b0b6cfa8ee06df3c8f Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Wed, 2 Sep 2020 17:40:43 -0700 Subject: [PATCH 125/151] Remove TODO --- .../lib/src/types/x_type_group/x_type_group.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart index 58126cdbd5c2..ed8ce9c2f396 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// TODO: should we be using this package or just extracting the important conversion data from it? - /// A set of allowed XTypes class XTypeGroup { /// Creates a new group with the given label and file extensions. From 8a2c11eac08d24816ea7527d1ade5b966935cd53 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Thu, 3 Sep 2020 17:48:20 -0700 Subject: [PATCH 126/151] Update platform interface --- .../method_channel_file_selector.dart | 55 +++- .../file_selector_interface.dart | 2 +- .../lib/src/types/x_file/io.dart | 4 +- .../src/types/x_type_group/x_type_group.dart | 11 + .../pubspec.yaml | 5 +- .../test/assets/hello.txt | 1 + ...file_selector_platform_interface_test.dart | 2 +- .../method_channel_file_selector_test.dart | 241 ++++++++++++++++++ .../test/x_file_html_test.dart | 108 ++++++++ .../test/x_file_io_test.dart | 99 +++++++ 10 files changed, 507 insertions(+), 21 deletions(-) create mode 100644 packages/file_selector/file_selector_platform_interface/test/assets/hello.txt create mode 100644 packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart create mode 100644 packages/file_selector/file_selector_platform_interface/test/x_file_html_test.dart create mode 100644 packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index 1a6ec05816a0..41ebdc9f3181 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -1,15 +1,20 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package:flutter/services.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:meta/meta.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); /// An implementation of [FileSelectorPlatform] that uses method channels. class MethodChannelFileSelector extends FileSelectorPlatform { + /// The MethodChannel that is being used by this implementation of the plugin. + @visibleForTesting + MethodChannel get channel => _channel; + /// Load a file from user's computer and return it as an XFile @override Future openFile({ @@ -17,15 +22,17 @@ class MethodChannelFileSelector extends FileSelectorPlatform { String initialDirectory, String confirmButtonText, }) async { - String path = await _channel.invokeMethod( - 'openFiles', - { - 'acceptedTypes': acceptedTypeGroups, + final List path = await _channel.invokeListMethod( + 'openFile', + { + 'acceptedTypeGroups': + acceptedTypeGroups?.map((group) => group.toJSON())?.toList(), 'initialDirectory': initialDirectory, + 'confirmButtonText': confirmButtonText, 'multiple': false, }, ); - return XFile(path); + return path == null ? null : XFile(path?.first); } /// Load multiple files from user's computer and return it as an XFile @@ -35,18 +42,20 @@ class MethodChannelFileSelector extends FileSelectorPlatform { String initialDirectory, String confirmButtonText, }) async { - final pathList = await _channel.invokeMethod>( - 'openFiles', - { - 'acceptedTypes': acceptedTypeGroups, + final List pathList = await _channel.invokeListMethod( + 'openFile', + { + 'acceptedTypeGroups': + acceptedTypeGroups?.map((group) => group.toJSON())?.toList(), 'initialDirectory': initialDirectory, + 'confirmButtonText': confirmButtonText, 'multiple': true, }, ); - return pathList.map((path) => XFile(path)).toList(); + return pathList?.map((path) => XFile(path))?.toList() ?? []; } - /// Saves the file to user's Disk + /// Gets the path from a save dialog @override Future getSavePath({ List acceptedTypeGroups, @@ -55,10 +64,28 @@ class MethodChannelFileSelector extends FileSelectorPlatform { String confirmButtonText, }) async { return _channel.invokeMethod( - 'saveFile', - { + 'getSavePath', + { + 'acceptedTypeGroups': + acceptedTypeGroups?.map((group) => group.toJSON())?.toList(), 'initialDirectory': initialDirectory, 'suggestedName': suggestedName, + 'confirmButtonText': confirmButtonText, + }, + ); + } + + /// Gets a directory path from a dialog + @override + Future getDirectoryPath({ + String initialDirectory, + String confirmButtonText, + }) async { + return _channel.invokeMethod( + 'getDirectoryPath', + { + 'initialDirectory': initialDirectory, + 'confirmButtonText': confirmButtonText, }, ); } diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index 53138122fcb5..675fe1ee2585 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart index bd8470a1d8ca..2067bbdfb140 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart @@ -35,7 +35,7 @@ class XFile extends XFileBase { int length, DateTime lastModified, }) : _bytes = bytes, - _file = File(path), + _file = File(path ?? ''), _length = length, _lastModified = lastModified, super(path) { @@ -55,7 +55,7 @@ class XFile extends XFileBase { @override void saveTo(String path) async { File fileToSave = File(path); - await fileToSave.writeAsBytes(_bytes); + await fileToSave.writeAsBytes(_bytes ?? (await readAsBytes())); await fileToSave.create(); } diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart index ed8ce9c2f396..b14a3fa235b2 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart @@ -42,4 +42,15 @@ class XTypeGroup { /// The web wild cards for this group (ex: image/*, video/*) final List webWildCards; + + /// Converts this object into a JSON formatted object + Map toJSON() { + return { + 'label': label, + 'extensions': extensions, + 'mimeTypes': mimeTypes, + 'macUTIs': macUTIs, + 'webWildCards': webWildCards, + }; + } } diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index 71aa2f06d2b6..be24867c84bc 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: file_selector_platform_interface -description: A common platform interface for the file_picker plugin. -homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_platform_interface +description: A common platform interface for the file_selector plugin. +homepage: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_platform_interface # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes version: 0.1.0 @@ -11,7 +11,6 @@ dependencies: meta: ^1.0.5 http: ^0.12.0+1 plugin_platform_interface: ^1.0.1 - mime_type: ^0.3.1 dev_dependencies: test: ^1.15.0 diff --git a/packages/file_selector/file_selector_platform_interface/test/assets/hello.txt b/packages/file_selector/file_selector_platform_interface/test/assets/hello.txt new file mode 100644 index 000000000000..5dd01c177f5d --- /dev/null +++ b/packages/file_selector/file_selector_platform_interface/test/assets/hello.txt @@ -0,0 +1 @@ +Hello, world! \ No newline at end of file diff --git a/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart b/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart index 465900fa429a..eb5bb89ad5b1 100644 --- a/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart b/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart new file mode 100644 index 000000000000..adb519248b3b --- /dev/null +++ b/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart @@ -0,0 +1,241 @@ +// Copyright 2020 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:file_selector_platform_interface/src/method_channel/method_channel_file_selector.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$MethodChannelFileSelector()', () { + MethodChannelFileSelector plugin = MethodChannelFileSelector(); + + final List log = []; + + setUp(() { + plugin.channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return null; + }); + + log.clear(); + }); + + group('#openFile', () { + test('passes the accepted type groups correctly', () async { + final group = XTypeGroup( + label: 'text', + extensions: ['.txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + webWildCards: ['document/*']); + + final groupTwo = XTypeGroup( + label: 'image', + extensions: ['.jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*']); + + await plugin.openFile(acceptedTypeGroups: [group, groupTwo]); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': [group.toJSON(), groupTwo.toJSON()], + 'initialDirectory': null, + 'confirmButtonText': null, + 'multiple': false, + }), + ], + ); + }); + test('passes initialDirectory correctly', () async { + await plugin.openFile(initialDirectory: "/example/directory"); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': null, + 'initialDirectory': "/example/directory", + 'confirmButtonText': null, + 'multiple': false, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.openFile(confirmButtonText: "Open File"); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': null, + 'initialDirectory': null, + 'confirmButtonText': "Open File", + 'multiple': false, + }), + ], + ); + }); + }); + group('#openFiles', () { + test('passes the accepted type groups correctly', () async { + final group = XTypeGroup( + label: 'text', + extensions: ['.txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + webWildCards: ['document/*']); + + final groupTwo = XTypeGroup( + label: 'image', + extensions: ['.jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*']); + + await plugin.openFiles(acceptedTypeGroups: [group, groupTwo]); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': [group.toJSON(), groupTwo.toJSON()], + 'initialDirectory': null, + 'confirmButtonText': null, + 'multiple': true, + }), + ], + ); + }); + test('passes initialDirectory correctly', () async { + await plugin.openFiles(initialDirectory: "/example/directory"); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': null, + 'initialDirectory': "/example/directory", + 'confirmButtonText': null, + 'multiple': true, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.openFiles(confirmButtonText: "Open File"); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': null, + 'initialDirectory': null, + 'confirmButtonText': "Open File", + 'multiple': true, + }), + ], + ); + }); + }); + + group('#getSavePath', () { + test('passes the accepted type groups correctly', () async { + final group = XTypeGroup( + label: 'text', + extensions: ['.txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + webWildCards: ['document/*']); + + final groupTwo = XTypeGroup( + label: 'image', + extensions: ['.jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*']); + + await plugin.getSavePath(acceptedTypeGroups: [group, groupTwo]); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'acceptedTypeGroups': [group.toJSON(), groupTwo.toJSON()], + 'initialDirectory': null, + 'suggestedName': null, + 'confirmButtonText': null, + }), + ], + ); + }); + test('passes initialDirectory correctly', () async { + await plugin.getSavePath(initialDirectory: "/example/directory"); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'acceptedTypeGroups': null, + 'initialDirectory': "/example/directory", + 'suggestedName': null, + 'confirmButtonText': null, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.getSavePath(confirmButtonText: "Open File"); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'acceptedTypeGroups': null, + 'initialDirectory': null, + 'suggestedName': null, + 'confirmButtonText': "Open File", + }), + ], + ); + }); + group('#getDirectoryPath', () { + test('passes initialDirectory correctly', () async { + await plugin.getDirectoryPath(initialDirectory: "/example/directory"); + + expect( + log, + [ + isMethodCall('getDirectoryPath', arguments: { + 'initialDirectory': "/example/directory", + 'confirmButtonText': null, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.getDirectoryPath(confirmButtonText: "Open File"); + + expect( + log, + [ + isMethodCall('getDirectoryPath', arguments: { + 'initialDirectory': null, + 'confirmButtonText': "Open File", + }), + ], + ); + }); + }); + }); + }); +} diff --git a/packages/file_selector/file_selector_platform_interface/test/x_file_html_test.dart b/packages/file_selector/file_selector_platform_interface/test/x_file_html_test.dart new file mode 100644 index 000000000000..08b9032006e9 --- /dev/null +++ b/packages/file_selector/file_selector_platform_interface/test/x_file_html_test.dart @@ -0,0 +1,108 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@TestOn('chrome') // Uses web-only Flutter SDK + +import 'dart:convert'; +import 'dart:html' as html; +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; + +import 'dart:html'; + +final String expectedStringContents = 'Hello, world!'; +final Uint8List bytes = utf8.encode(expectedStringContents); +final html.File textFile = html.File([bytes], 'hello.txt'); +final String textFileUrl = html.Url.createObjectUrl(textFile); + +void main() { + group('Create with an objectUrl', () { + final file = XFile(textFileUrl); + + test('Can be read as a string', () async { + expect(await file.readAsString(), equals(expectedStringContents)); + }); + test('Can be read as bytes', () async { + expect(await file.readAsBytes(), equals(bytes)); + }); + + test('Can be read as a stream', () async { + expect(await file.openRead().first, equals(bytes)); + }); + + test('Stream can be sliced', () async { + expect(await file.openRead(2, 5).first, equals(bytes.sublist(2, 5))); + }); + }); + + group('Create from data', () { + final file = XFile.fromData(bytes); + + test('Can be read as a string', () async { + expect(await file.readAsString(), equals(expectedStringContents)); + }); + test('Can be read as bytes', () async { + expect(await file.readAsBytes(), equals(bytes)); + }); + + test('Can be read as a stream', () async { + expect(await file.openRead().first, equals(bytes)); + }); + + test('Stream can be sliced', () async { + expect(await file.openRead(2, 5).first, equals(bytes.sublist(2, 5))); + }); + }); + + group('saveTo(..)', () { + final String xFileDomElementId = '__x_file_dom_element'; + + group('XFile saveTo(..)', () { + test('creates a DOM container', () async { + XFile file = XFile.fromData(bytes); + + await file.saveTo(''); + + final container = querySelector('#${xFileDomElementId}'); + + expect(container, isNotNull); + }); + + test('create anchor element', () async { + XFile file = XFile.fromData(bytes, name: textFile.name); + + await file.saveTo('path'); + + final container = querySelector('#${xFileDomElementId}'); + final AnchorElement element = container?.children?.firstWhere( + (element) => element.tagName == 'A', + orElse: () => null); + + expect(element, isNotNull); + expect(element.href, file.path); + expect(element.download, file.name); + }); + + test('anchor element is clicked', () async { + final mockAnchor = AnchorElement(); + + XFileTestOverrides overrides = XFileTestOverrides( + createAnchorElement: (_, __) => mockAnchor, + ); + + XFile file = + XFile.fromData(bytes, name: textFile.name, overrides: overrides); + + bool clicked = false; + mockAnchor.onClick.listen((event) => clicked = true); + + await file.saveTo('path'); + + expect(clicked, true); + }); + }); + }); +} diff --git a/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart b/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart new file mode 100644 index 000000000000..af9639a32c29 --- /dev/null +++ b/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart @@ -0,0 +1,99 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@TestOn('vm') // Uses dart:io + +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; + +// Please note that executing this test with command +// `flutter test test/x_file_io_test.dart` will set the directory +// to ./file_selector_platform_interface. +// +// This will cause our hello.txt file to be not be found. Please +// execute this test with `flutter test` or change the path to +// ./test/assets/hello.txt +// +// https://github.com/flutter/flutter/issues/20907 + +final pathPrefix = './assets/'; +final path = pathPrefix + 'hello.txt'; +final String expectedStringContents = 'Hello, world!'; +final Uint8List bytes = utf8.encode(expectedStringContents); +final File textFile = File(path); +final String textFilePath = textFile.path; + +void main() { + group('Create with a path', () { + final file = XFile(textFilePath); + + test('Can be read as a string', () async { + expect(await file.readAsString(), equals(expectedStringContents)); + }); + test('Can be read as bytes', () async { + expect(await file.readAsBytes(), equals(bytes)); + }); + + test('Can be read as a stream', () async { + expect(await file.openRead().first, equals(bytes)); + }); + + test('Stream can be sliced', () async { + expect(await file.openRead(2, 5).first, equals(bytes.sublist(2, 5))); + }); + + test('saveTo(..) creates file', () async { + File removeBeforeTest = File(pathPrefix + 'newFilePath.txt'); + if (removeBeforeTest.existsSync()) { + await removeBeforeTest.delete(); + } + + await file.saveTo(pathPrefix + 'newFilePath.txt'); + File newFile = File(pathPrefix + 'newFilePath.txt'); + + expect(newFile.existsSync(), isTrue); + expect(newFile.readAsStringSync(), 'Hello, world!'); + + await newFile.delete(); + }); + }); + + group('Create with data', () { + final file = XFile.fromData(bytes); + + test('Can be read as a string', () async { + expect(await file.readAsString(), equals(expectedStringContents)); + }); + test('Can be read as bytes', () async { + expect(await file.readAsBytes(), equals(bytes)); + }); + + test('Can be read as a stream', () async { + expect(await file.openRead().first, equals(bytes)); + }); + + test('Stream can be sliced', () async { + expect(await file.openRead(2, 5).first, equals(bytes.sublist(2, 5))); + }); + + test('Function saveTo(..) creates file', () async { + File removeBeforeTest = File(pathPrefix + 'newFileData.txt'); + if (removeBeforeTest.existsSync()) { + await removeBeforeTest.delete(); + } + + await file.saveTo(pathPrefix + 'newFileData.txt'); + File newFile = File(pathPrefix + 'newFileData.txt'); + + expect(newFile.existsSync(), isTrue); + expect(newFile.readAsStringSync(), 'Hello, world!'); + + await newFile.delete(); + }); + }); +} From cc2989814c2ed35910c7d8e06d4e1fce14e626e2 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Thu, 3 Sep 2020 17:56:46 -0700 Subject: [PATCH 127/151] Update Coprights --- packages/file_selector/file_selector/LICENSE | 2 +- packages/file_selector/file_selector/lib/file_selector.dart | 2 +- .../file_selector/file_selector/test/file_selector_test.dart | 2 +- packages/file_selector/file_selector_platform_interface/LICENSE | 2 +- .../lib/src/method_channel/method_channel_file_selector.dart | 2 +- .../lib/src/platform_interface/file_selector_interface.dart | 2 +- .../test/file_selector_platform_interface_test.dart | 2 +- .../file_selector_platform_interface/test/x_file_html_test.dart | 2 +- .../file_selector_platform_interface/test/x_file_io_test.dart | 2 +- packages/file_selector/file_selector_web/LICENSE | 2 +- packages/file_selector/file_selector_web/test/lib/main.dart | 2 +- .../test/test_driver/web_integration_test.dart | 2 +- packages/file_selector/file_selector_web/test/web/index.html | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/file_selector/file_selector/LICENSE b/packages/file_selector/file_selector/LICENSE index 000b4618d2bd..50b00c044f2e 100644 --- a/packages/file_selector/file_selector/LICENSE +++ b/packages/file_selector/file_selector/LICENSE @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2020 The Flutter Authors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index 54c012f57a4a..080eac4460ac 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2020 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index 024c8c8d314f..44215ecbf020 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -1,4 +1,4 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2020 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/file_selector/file_selector_platform_interface/LICENSE b/packages/file_selector/file_selector_platform_interface/LICENSE index 0c91662b3f2f..47bd8aed523c 100644 --- a/packages/file_selector/file_selector_platform_interface/LICENSE +++ b/packages/file_selector/file_selector_platform_interface/LICENSE @@ -1,4 +1,4 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. +// Copyright 2020 The Flutter Authors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index 41ebdc9f3181..cc1264e520c6 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -1,4 +1,4 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. +// Copyright 2020 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index 675fe1ee2585..0f0cc0298777 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -1,4 +1,4 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. +// Copyright 2020 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart b/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart index eb5bb89ad5b1..6809cee66963 100644 --- a/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart @@ -1,4 +1,4 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. +// Copyright 2020 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/file_selector/file_selector_platform_interface/test/x_file_html_test.dart b/packages/file_selector/file_selector_platform_interface/test/x_file_html_test.dart index 08b9032006e9..f888a0486ca7 100644 --- a/packages/file_selector/file_selector_platform_interface/test/x_file_html_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/x_file_html_test.dart @@ -1,4 +1,4 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. +// Copyright 2020 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart b/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart index af9639a32c29..518a44d4a7da 100644 --- a/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart @@ -1,4 +1,4 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. +// Copyright 2020 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/file_selector/file_selector_web/LICENSE b/packages/file_selector/file_selector_web/LICENSE index 0c382ce171cc..47bd8aed523c 100644 --- a/packages/file_selector/file_selector_web/LICENSE +++ b/packages/file_selector/file_selector_web/LICENSE @@ -1,4 +1,4 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. +// Copyright 2020 The Flutter Authors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are diff --git a/packages/file_selector/file_selector_web/test/lib/main.dart b/packages/file_selector/file_selector_web/test/lib/main.dart index 84c77ea76f01..29bd9078749c 100644 --- a/packages/file_selector/file_selector_web/test/lib/main.dart +++ b/packages/file_selector/file_selector_web/test/lib/main.dart @@ -1,4 +1,4 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. +// Copyright 2020 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_integration_test.dart b/packages/file_selector/file_selector_web/test/test_driver/web_integration_test.dart index f26b6a310cfe..1c5d1fcbcca7 100644 --- a/packages/file_selector/file_selector_web/test/test_driver/web_integration_test.dart +++ b/packages/file_selector/file_selector_web/test/test_driver/web_integration_test.dart @@ -1,4 +1,4 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. +// Copyright 2020 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/packages/file_selector/file_selector_web/test/web/index.html b/packages/file_selector/file_selector_web/test/web/index.html index 59a832b5de4c..b7d4fb222a25 100644 --- a/packages/file_selector/file_selector_web/test/web/index.html +++ b/packages/file_selector/file_selector_web/test/web/index.html @@ -1,5 +1,5 @@ - From 0a3732c3a8f26a1e77320b0803a2f944be05edb8 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 8 Sep 2020 12:56:11 -0700 Subject: [PATCH 128/151] Add XTypeGroup tests and remove document/* as a web wild card --- .../method_channel_file_selector_test.dart | 30 +++++++-------- .../test/x_file_io_test.dart | 4 +- .../test/x_type_group_test.dart | 37 +++++++++++++++++++ 3 files changed, 54 insertions(+), 17 deletions(-) create mode 100644 packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart diff --git a/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart b/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart index adb519248b3b..99f9fe0f0e3b 100644 --- a/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart @@ -28,11 +28,11 @@ void main() { group('#openFile', () { test('passes the accepted type groups correctly', () async { final group = XTypeGroup( - label: 'text', - extensions: ['.txt'], - mimeTypes: ['text/plain'], - macUTIs: ['public.text'], - webWildCards: ['document/*']); + label: 'text', + extensions: ['.txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); final groupTwo = XTypeGroup( label: 'image', @@ -89,11 +89,11 @@ void main() { group('#openFiles', () { test('passes the accepted type groups correctly', () async { final group = XTypeGroup( - label: 'text', - extensions: ['.txt'], - mimeTypes: ['text/plain'], - macUTIs: ['public.text'], - webWildCards: ['document/*']); + label: 'text', + extensions: ['.txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); final groupTwo = XTypeGroup( label: 'image', @@ -151,11 +151,11 @@ void main() { group('#getSavePath', () { test('passes the accepted type groups correctly', () async { final group = XTypeGroup( - label: 'text', - extensions: ['.txt'], - mimeTypes: ['text/plain'], - macUTIs: ['public.text'], - webWildCards: ['document/*']); + label: 'text', + extensions: ['.txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); final groupTwo = XTypeGroup( label: 'image', diff --git a/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart b/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart index 518a44d4a7da..b669324462b2 100644 --- a/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart @@ -16,8 +16,8 @@ import 'package:file_selector_platform_interface/file_selector_platform_interfac // to ./file_selector_platform_interface. // // This will cause our hello.txt file to be not be found. Please -// execute this test with `flutter test` or change the path to -// ./test/assets/hello.txt +// execute this test with `flutter test` or change the path prefix +// to ./test/assets/ // // https://github.com/flutter/flutter/issues/20907 diff --git a/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart b/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart new file mode 100644 index 000000000000..877e530cb342 --- /dev/null +++ b/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart @@ -0,0 +1,37 @@ +// Copyright 2020 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; + +void main() { + group('XTypeGroup', () { + test('fails assertion with no parameters set', () { + expect(() => XTypeGroup(), throwsAssertionError); + }); + + test('toJSON() creates correct map', () { + final label = 'test group'; + final extensions = ['.txt', '.jpg']; + final mimeTypes = ['text/plain']; + final macUTIs = ['public.plain-text']; + final webWildCards = ['image/*']; + + final group = XTypeGroup( + label: label, + extensions: extensions, + mimeTypes: mimeTypes, + macUTIs: macUTIs, + webWildCards: webWildCards, + ); + + final jsonMap = group.toJSON(); + expect(jsonMap['label'], label); + expect(jsonMap['extensions'], extensions); + expect(jsonMap['mimeTypes'], mimeTypes); + expect(jsonMap['macUTIs'], macUTIs); + expect(jsonMap['webWildCards'], webWildCards); + }); + }); +} From 13789140ed66445934148f73ad6e69b1bbb921d2 Mon Sep 17 00:00:00 2001 From: Jason Panelli Date: Tue, 8 Sep 2020 14:24:14 -0700 Subject: [PATCH 129/151] Update Licenses --- packages/file_selector/file_selector/LICENSE | 52 +++++++++---------- .../file_selector_platform_interface/LICENSE | 52 +++++++++---------- .../src/types/x_type_group/x_type_group.dart | 16 ++---- .../file_selector/file_selector_web/LICENSE | 52 +++++++++---------- 4 files changed, 78 insertions(+), 94 deletions(-) diff --git a/packages/file_selector/file_selector/LICENSE b/packages/file_selector/file_selector/LICENSE index 50b00c044f2e..2c91f1438173 100644 --- a/packages/file_selector/file_selector/LICENSE +++ b/packages/file_selector/file_selector/LICENSE @@ -1,27 +1,25 @@ -// Copyright 2020 The Flutter Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +Copyright 2020 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/packages/file_selector/file_selector_platform_interface/LICENSE b/packages/file_selector/file_selector_platform_interface/LICENSE index 47bd8aed523c..2c91f1438173 100644 --- a/packages/file_selector/file_selector_platform_interface/LICENSE +++ b/packages/file_selector/file_selector_platform_interface/LICENSE @@ -1,27 +1,25 @@ -// Copyright 2020 The Flutter Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Copyright 2020 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart index b14a3fa235b2..a7d52a766498 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart @@ -1,16 +1,6 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Copyright 2020 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. /// A set of allowed XTypes class XTypeGroup { diff --git a/packages/file_selector/file_selector_web/LICENSE b/packages/file_selector/file_selector_web/LICENSE index 47bd8aed523c..2c91f1438173 100644 --- a/packages/file_selector/file_selector_web/LICENSE +++ b/packages/file_selector/file_selector_web/LICENSE @@ -1,27 +1,25 @@ -// Copyright 2020 The Flutter Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Copyright 2020 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file From 5f144df1d64861966aa7ce404a604bfe424e7ea8 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Sun, 11 Oct 2020 18:57:49 -0500 Subject: [PATCH 130/151] Removes file_selector_platform_interface and file_selector_web --- .../CHANGELOG.md | 3 - .../file_selector_platform_interface/LICENSE | 25 -- .../README.md | 26 -- .../lib/file_selector_platform_interface.dart | 2 - .../method_channel_file_selector.dart | 92 ------ .../file_selector_interface.dart | 74 ----- .../lib/src/types/types.dart | 3 - .../lib/src/types/x_file/base.dart | 82 ------ .../lib/src/types/x_file/html.dart | 132 --------- .../lib/src/types/x_file/io.dart | 106 ------- .../lib/src/types/x_file/unsupported.dart | 54 ---- .../lib/src/types/x_file/x_file.dart | 3 - .../src/types/x_type_group/x_type_group.dart | 46 --- .../lib/src/web_helpers/web_helpers.dart | 34 --- .../pubspec.yaml | 24 -- .../test/assets/hello.txt | 1 - ...file_selector_platform_interface_test.dart | 43 --- .../method_channel_file_selector_test.dart | 241 ---------------- .../test/x_file_html_test.dart | 108 ------- .../test/x_file_io_test.dart | 99 ------- .../test/x_type_group_test.dart | 37 --- .../file_selector_web/CHANGELOG.md | 3 - .../file_selector/file_selector_web/LICENSE | 25 -- .../file_selector/file_selector_web/README.md | 37 --- .../lib/file_selector_web.dart | 197 ------------- .../file_selector_web/pubspec.yaml | 36 --- .../file_selector_web/test/README.md | 14 - .../file_selector_web/test/lib/main.dart | 23 -- .../file_selector_web/test/pubspec.yaml | 24 -- .../test/test_driver/web_integration.dart | 269 ------------------ .../test_driver/web_integration_test.dart | 7 - .../file_selector_web/test/web/index.html | 13 - 32 files changed, 1883 deletions(-) delete mode 100644 packages/file_selector/file_selector_platform_interface/CHANGELOG.md delete mode 100644 packages/file_selector/file_selector_platform_interface/LICENSE delete mode 100644 packages/file_selector/file_selector_platform_interface/README.md delete mode 100644 packages/file_selector/file_selector_platform_interface/lib/file_selector_platform_interface.dart delete mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart delete mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart delete mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart delete mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart delete mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart delete mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart delete mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart delete mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart delete mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart delete mode 100644 packages/file_selector/file_selector_platform_interface/lib/src/web_helpers/web_helpers.dart delete mode 100644 packages/file_selector/file_selector_platform_interface/pubspec.yaml delete mode 100644 packages/file_selector/file_selector_platform_interface/test/assets/hello.txt delete mode 100644 packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart delete mode 100644 packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart delete mode 100644 packages/file_selector/file_selector_platform_interface/test/x_file_html_test.dart delete mode 100644 packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart delete mode 100644 packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart delete mode 100644 packages/file_selector/file_selector_web/CHANGELOG.md delete mode 100644 packages/file_selector/file_selector_web/LICENSE delete mode 100644 packages/file_selector/file_selector_web/README.md delete mode 100644 packages/file_selector/file_selector_web/lib/file_selector_web.dart delete mode 100644 packages/file_selector/file_selector_web/pubspec.yaml delete mode 100644 packages/file_selector/file_selector_web/test/README.md delete mode 100644 packages/file_selector/file_selector_web/test/lib/main.dart delete mode 100644 packages/file_selector/file_selector_web/test/pubspec.yaml delete mode 100644 packages/file_selector/file_selector_web/test/test_driver/web_integration.dart delete mode 100644 packages/file_selector/file_selector_web/test/test_driver/web_integration_test.dart delete mode 100644 packages/file_selector/file_selector_web/test/web/index.html diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md deleted file mode 100644 index 6073234226b2..000000000000 --- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 0.1.0 - -* Initial release. diff --git a/packages/file_selector/file_selector_platform_interface/LICENSE b/packages/file_selector/file_selector_platform_interface/LICENSE deleted file mode 100644 index 2c91f1438173..000000000000 --- a/packages/file_selector/file_selector_platform_interface/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2020 The Flutter Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/packages/file_selector/file_selector_platform_interface/README.md b/packages/file_selector/file_selector_platform_interface/README.md deleted file mode 100644 index d750461f2133..000000000000 --- a/packages/file_selector/file_selector_platform_interface/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# file_selector_platform_interface - -A common platform interface for the `file_selector` plugin. - -This interface allows platform-specific implementations of the `file_selector` -plugin, as well as the plugin itself, to ensure they are supporting the -same interface. - -# Usage - -To implement a new platform-specific implementation of `file_selector`, extend -[`FileSelectorPlatform`][2] with an implementation that performs the -platform-specific behavior, and when you register your plugin, set the default -`FileSelectorPlatform` by calling -`FileSelectorPlatform.instance = MyPlatformFileSelector()`. - -# Note on breaking changes - -Strongly prefer non-breaking changes (such as adding a method to the interface) -over breaking changes for this package. - -See https://flutter.dev/go/platform-interface-breaking-changes for a discussion -on why a less-clean interface is preferable to a breaking change. - -[1]: ../file_selector -[2]: lib/file_selector_platform_interface.dart diff --git a/packages/file_selector/file_selector_platform_interface/lib/file_selector_platform_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/file_selector_platform_interface.dart deleted file mode 100644 index 69e3064150b5..000000000000 --- a/packages/file_selector/file_selector_platform_interface/lib/file_selector_platform_interface.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'src/platform_interface/file_selector_interface.dart'; -export 'src/types/types.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart deleted file mode 100644 index cc1264e520c6..000000000000 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2020 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/services.dart'; - -import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; -import 'package:meta/meta.dart'; - -const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_picker'); - -/// An implementation of [FileSelectorPlatform] that uses method channels. -class MethodChannelFileSelector extends FileSelectorPlatform { - /// The MethodChannel that is being used by this implementation of the plugin. - @visibleForTesting - MethodChannel get channel => _channel; - - /// Load a file from user's computer and return it as an XFile - @override - Future openFile({ - List acceptedTypeGroups, - String initialDirectory, - String confirmButtonText, - }) async { - final List path = await _channel.invokeListMethod( - 'openFile', - { - 'acceptedTypeGroups': - acceptedTypeGroups?.map((group) => group.toJSON())?.toList(), - 'initialDirectory': initialDirectory, - 'confirmButtonText': confirmButtonText, - 'multiple': false, - }, - ); - return path == null ? null : XFile(path?.first); - } - - /// Load multiple files from user's computer and return it as an XFile - @override - Future> openFiles({ - List acceptedTypeGroups, - String initialDirectory, - String confirmButtonText, - }) async { - final List pathList = await _channel.invokeListMethod( - 'openFile', - { - 'acceptedTypeGroups': - acceptedTypeGroups?.map((group) => group.toJSON())?.toList(), - 'initialDirectory': initialDirectory, - 'confirmButtonText': confirmButtonText, - 'multiple': true, - }, - ); - return pathList?.map((path) => XFile(path))?.toList() ?? []; - } - - /// Gets the path from a save dialog - @override - Future getSavePath({ - List acceptedTypeGroups, - String initialDirectory, - String suggestedName, - String confirmButtonText, - }) async { - return _channel.invokeMethod( - 'getSavePath', - { - 'acceptedTypeGroups': - acceptedTypeGroups?.map((group) => group.toJSON())?.toList(), - 'initialDirectory': initialDirectory, - 'suggestedName': suggestedName, - 'confirmButtonText': confirmButtonText, - }, - ); - } - - /// Gets a directory path from a dialog - @override - Future getDirectoryPath({ - String initialDirectory, - String confirmButtonText, - }) async { - return _channel.invokeMethod( - 'getDirectoryPath', - { - 'initialDirectory': initialDirectory, - 'confirmButtonText': confirmButtonText, - }, - ); - } -} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart deleted file mode 100644 index 0f0cc0298777..000000000000 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2020 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -import '../method_channel/method_channel_file_selector.dart'; - -/// 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 -/// [FileSelectorPlatform] methods. -abstract class FileSelectorPlatform extends PlatformInterface { - /// Constructs a FileSelectorPlatform. - FileSelectorPlatform() : super(token: _token); - - static final Object _token = Object(); - - static FileSelectorPlatform _instance = MethodChannelFileSelector(); - - /// The default instance of [FileSelectorPlatform] to use. - /// - /// Defaults to [MethodChannelFileSelector]. - static FileSelectorPlatform get instance => _instance; - - /// Platform-specific plugins should set this with their own platform-specific - /// class that extends [FileSelectorPlatform] when they register themselves. - static set instance(FileSelectorPlatform instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - /// Open file dialog for loading files and return a file path - Future openFile({ - List acceptedTypeGroups, - String initialDirectory, - String confirmButtonText, - }) { - throw UnimplementedError('openFile() has not been implemented.'); - } - - /// Open file dialog for loading files and return a list of file paths - Future> openFiles({ - List acceptedTypeGroups, - String initialDirectory, - String confirmButtonText, - }) { - throw UnimplementedError('openFiles() has not been implemented.'); - } - - /// Open file dialog for saving files and return a file path at which to save - Future getSavePath({ - List acceptedTypeGroups, - String initialDirectory, - String suggestedName, - String confirmButtonText, - }) { - throw UnimplementedError('getSavePath() has not been implemented.'); - } - - /// Open file dialog for loading directories and return a directory path - Future getDirectoryPath({ - String initialDirectory, - String confirmButtonText, - }) { - throw UnimplementedError('getDirectoryPath() has not been implemented.'); - } -} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart deleted file mode 100644 index 8848c6751ba3..000000000000 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/types.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'x_file/x_file.dart'; - -export 'x_type_group/x_type_group.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart deleted file mode 100644 index 7ea050ff28db..000000000000 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/base.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'dart:convert'; -import 'dart:typed_data'; - -/// The interface for a XFile. -/// -/// A XFile is a container that wraps the path of a selected -/// file by the user and (in some platforms, like web) the bytes -/// with the contents of the file. -/// -/// This class is a very limited subset of dart:io [File], so all -/// the methods should seem familiar. -abstract class XFileBase { - /// Construct a XFile - XFileBase(String path); - - /// Save the XFile at the indicated file path. - void saveTo(String path) async { - throw UnimplementedError('saveTo has not been implemented.'); - } - - /// Get the path of the picked file. - /// - /// This should only be used as a backwards-compatibility clutch - /// for mobile apps, or cosmetic reasons only (to show the user - /// the path they've picked). - /// - /// Accessing the data contained in the picked file by its path - /// is platform-dependant (and won't work on web), so use the - /// byte getters in the XFile instance instead. - String get path { - throw UnimplementedError('.path has not been implemented.'); - } - - /// The name of the file as it was selected by the user in their device. - /// - /// Use only for cosmetic reasons, do not try to use this as a path. - String get name { - throw UnimplementedError('.name has not been implemented.'); - } - - /// For web, it may be necessary for a file to know its MIME type. - String get mimeType { - throw UnimplementedError('.mimeType has not been implemented.'); - } - - /// Get the length of the file. Returns a `Future` that completes with the length in bytes. - Future length() { - throw UnimplementedError('.length() has not been implemented.'); - } - - /// Synchronously read the entire file contents as a string using the given [Encoding]. - /// - /// By default, `encoding` is [utf8]. - /// - /// Throws Exception if the operation fails. - Future readAsString({Encoding encoding = utf8}) { - throw UnimplementedError('readAsString() has not been implemented.'); - } - - /// Synchronously read the entire file contents as a list of bytes. - /// - /// Throws Exception if the operation fails. - Future readAsBytes() { - throw UnimplementedError('readAsBytes() has not been implemented.'); - } - - /// Create a new independent [Stream] for the contents of this file. - /// - /// If `start` is present, the file will be read from byte-offset `start`. Otherwise from the beginning (index 0). - /// - /// If `end` is present, only up to byte-index `end` will be read. Otherwise, until end of file. - /// - /// In order to make sure that system resources are freed, the stream must be read to completion or the subscription on the stream must be cancelled. - Stream openRead([int start, int end]) { - throw UnimplementedError('openRead() has not been implemented.'); - } - - /// Get the last-modified time for the XFile - Future lastModified() { - throw UnimplementedError('openRead() has not been implemented.'); - } -} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart deleted file mode 100644 index fe898eb4ca62..000000000000 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/html.dart +++ /dev/null @@ -1,132 +0,0 @@ -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:http/http.dart' as http show readBytes; -import 'package:meta/meta.dart'; -import 'dart:html'; - -import '../../web_helpers/web_helpers.dart'; -import './base.dart'; - -/// A XFile that works on web. -/// -/// It wraps the bytes of a selected file. -class XFile extends XFileBase { - String path; - - final String mimeType; - final Uint8List _data; - final int _length; - final String name; - final DateTime _lastModified; - Element _target; - - final XFileTestOverrides _overrides; - - bool get _hasTestOverrides => _overrides != null; - - /// Construct a XFile object from its ObjectUrl. - /// - /// Optionally, this can be initialized with `bytes` and `length` - /// so no http requests are performed to retrieve files later. - /// - /// `name` needs to be passed from the outside, since we only have - /// access to it while we create the ObjectUrl. - XFile( - this.path, { - this.mimeType, - this.name, - int length, - Uint8List bytes, - DateTime lastModified, - @visibleForTesting XFileTestOverrides overrides, - }) : _data = bytes, - _length = length, - _overrides = overrides, - _lastModified = lastModified, - super(path); - - /// Construct an XFile from its data - XFile.fromData( - Uint8List bytes, { - this.mimeType, - this.name, - int length, - DateTime lastModified, - this.path, - @visibleForTesting XFileTestOverrides overrides, - }) : _data = bytes, - _length = length, - _overrides = overrides, - _lastModified = lastModified, - super(path) { - if (path == null) { - final blob = (mimeType == null) ? Blob([bytes]) : Blob([bytes], mimeType); - this.path = Url.createObjectUrl(blob); - } - } - - @override - Future lastModified() async { - if (_lastModified != null) { - return Future.value(_lastModified); - } - return null; - } - - Future get _bytes async { - if (_data != null) { - return Future.value(UnmodifiableUint8ListView(_data)); - } - return http.readBytes(path); - } - - @override - Future length() async { - return _length ?? (await _bytes).length; - } - - @override - Future readAsString({Encoding encoding = utf8}) async { - return encoding.decode(await _bytes); - } - - @override - Future readAsBytes() async { - return Future.value(await _bytes); - } - - @override - Stream openRead([int start, int end]) async* { - final bytes = await _bytes; - yield bytes.sublist(start ?? 0, end ?? bytes.length); - } - - /// Saves the data of this XFile at the location indicated by path. - /// For the web implementation, the path variable is ignored. - void saveTo(String path) async { - // Create a DOM container where we can host the anchor. - _target = ensureInitialized('__x_file_dom_element'); - - // Create an tag with the appropriate download attributes and click it - // May be overridden with XFileTestOverrides - final AnchorElement element = - (_hasTestOverrides && _overrides.createAnchorElement != null) - ? _overrides.createAnchorElement(this.path, this.name) - : createAnchorElement(this.path, this.name); - - // Clear the children in our container so we can add an element to click - _target.children.clear(); - addElementToContainerAndClick(_target, element); - } -} - -/// Overrides some functions to allow testing -@visibleForTesting -class XFileTestOverrides { - /// For overriding the creation of the file input element. - Element Function(String href, String suggestedName) createAnchorElement; - - /// Default constructor for overrides - XFileTestOverrides({this.createAnchorElement}); -} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart deleted file mode 100644 index 2067bbdfb140..000000000000 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/io.dart +++ /dev/null @@ -1,106 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; -import 'dart:typed_data'; - -import './base.dart'; - -/// A XFile backed by a dart:io File. -class XFile extends XFileBase { - final File _file; - final String mimeType; - final DateTime _lastModified; - int _length; - - final Uint8List _bytes; - - /// Construct a XFile object backed by a dart:io File. - XFile( - String path, { - this.mimeType, - String name, - int length, - Uint8List bytes, - DateTime lastModified, - }) : _file = File(path), - _bytes = null, - _lastModified = lastModified, - super(path); - - /// Construct an XFile from its data - XFile.fromData( - Uint8List bytes, { - this.mimeType, - String path, - String name, - int length, - DateTime lastModified, - }) : _bytes = bytes, - _file = File(path ?? ''), - _length = length, - _lastModified = lastModified, - super(path) { - if (length == null) { - _length = bytes.length; - } - } - - @override - Future lastModified() { - if (_lastModified != null) { - return Future.value(_lastModified); - } - return _file.lastModified(); - } - - @override - void saveTo(String path) async { - File fileToSave = File(path); - await fileToSave.writeAsBytes(_bytes ?? (await readAsBytes())); - await fileToSave.create(); - } - - @override - String get path { - return _file.path; - } - - @override - String get name { - return _file.path.split(Platform.pathSeparator).last; - } - - @override - Future length() { - if (_length != null) { - return Future.value(_length); - } - return _file.length(); - } - - @override - Future readAsString({Encoding encoding = utf8}) { - if (_bytes != null) { - return Future.value(String.fromCharCodes(_bytes)); - } - return _file.readAsString(encoding: encoding); - } - - @override - Future readAsBytes() { - if (_bytes != null) { - return Future.value(_bytes); - } - return _file.readAsBytes(); - } - - @override - Stream openRead([int start, int end]) async* { - if (_bytes != null) { - final bytes = _bytes; - yield bytes.sublist(start ?? 0, end ?? bytes.length); - } - yield* _file - .openRead(start ?? 0, end) - .map((chunk) => Uint8List.fromList(chunk)); - } -} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart deleted file mode 100644 index f5fe388e0899..000000000000 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/unsupported.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'dart:typed_data'; -import 'package:meta/meta.dart'; - -import './base.dart'; - -/// A XFile is a cross-platform, simplified File abstraction. -/// -/// It wraps the bytes of a selected file, and its (platform-dependant) path. -class XFile extends XFileBase { - /// Construct a XFile object from its path. - /// - /// Optionally, this can be initialized with `bytes` and `length` - /// so no http requests are performed to retrieve data later. - /// - /// `name` may be passed from the outside, for those cases where the effective - /// `path` of the file doesn't match what the user sees when selecting it - /// (like in web) - XFile( - String path, { - String mimeType, - String name, - int length, - Uint8List bytes, - DateTime lastModified, - @visibleForTesting XFileTestOverrides overrides, - }) : super(path) { - throw UnimplementedError( - 'XFile is not available in your current platform.'); - } - - /// Construct a XFile object from its data - XFile.fromData( - Uint8List bytes, { - String mimeType, - String name, - int length, - DateTime lastModified, - String path, - @visibleForTesting XFileTestOverrides overrides, - }) : super(path) { - throw UnimplementedError( - 'XFile is not available in your current platform.'); - } -} - -/// Overrides some functions of XFile for testing purposes -@visibleForTesting -class XFileTestOverrides { - /// For overriding the creation of the file input element. - dynamic Function(String href, String suggestedName) createAnchorElement; - - /// Default constructor for overrides - XFileTestOverrides({this.createAnchorElement}); -} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart deleted file mode 100644 index f966a7c9a3aa..000000000000 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_file/x_file.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'unsupported.dart' - if (dart.library.html) 'html.dart' - if (dart.library.io) 'io.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart deleted file mode 100644 index a7d52a766498..000000000000 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2020 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/// A set of allowed XTypes -class XTypeGroup { - /// Creates a new group with the given label and file extensions. - XTypeGroup({ - this.label, - this.extensions, - this.mimeTypes, - this.macUTIs, - this.webWildCards, - }) : assert( - !((extensions == null || extensions.isEmpty) && - (mimeTypes == null || mimeTypes.isEmpty) && - (macUTIs == null || macUTIs.isEmpty) && - (webWildCards == null || webWildCards.isEmpty)), - "At least one type must be provided for an XTypeGroup."); - - /// The 'name' or reference to this group of types - final String label; - - /// The extensions for this group - final List extensions; - - /// The MIME types for this group - final List mimeTypes; - - /// The UTIs for this group - final List macUTIs; - - /// The web wild cards for this group (ex: image/*, video/*) - final List webWildCards; - - /// Converts this object into a JSON formatted object - Map toJSON() { - return { - 'label': label, - 'extensions': extensions, - 'mimeTypes': mimeTypes, - 'macUTIs': macUTIs, - 'webWildCards': webWildCards, - }; - } -} diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/web_helpers/web_helpers.dart b/packages/file_selector/file_selector_platform_interface/lib/src/web_helpers/web_helpers.dart deleted file mode 100644 index 9e40e562bc9a..000000000000 --- a/packages/file_selector/file_selector_platform_interface/lib/src/web_helpers/web_helpers.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'dart:html'; - -/// Create anchor element with download attribute -AnchorElement createAnchorElement(String href, String suggestedName) { - final element = AnchorElement(href: href); - - if (suggestedName == null) { - element.download = 'download'; - } else { - element.download = suggestedName; - } - - return element; -} - -/// Add an element to a container and click it -void addElementToContainerAndClick(Element container, Element element) { - // Add the element and click it - // All previous elements will be removed before adding the new one - container.children.add(element); - element.click(); -} - -/// Initializes a DOM container where we can host elements. -Element ensureInitialized(String id) { - var target = querySelector('#${id}'); - if (target == null) { - final Element targetElement = Element.tag('flt-x-file')..id = id; - - querySelector('body').children.add(targetElement); - target = targetElement; - } - return target; -} diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml deleted file mode 100644 index be24867c84bc..000000000000 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: file_selector_platform_interface -description: A common platform interface for the file_selector plugin. -homepage: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_platform_interface -# NOTE: We strongly prefer non-breaking changes, even at the expense of a -# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 0.1.0 - -dependencies: - flutter: - sdk: flutter - meta: ^1.0.5 - http: ^0.12.0+1 - plugin_platform_interface: ^1.0.1 - -dev_dependencies: - test: ^1.15.0 - flutter_test: - sdk: flutter - mockito: ^4.1.1 - pedantic: ^1.8.0 - -environment: - sdk: ">=2.1.0 <3.0.0" - flutter: ">=1.9.1+hotfix.4 <2.0.0" diff --git a/packages/file_selector/file_selector_platform_interface/test/assets/hello.txt b/packages/file_selector/file_selector_platform_interface/test/assets/hello.txt deleted file mode 100644 index 5dd01c177f5d..000000000000 --- a/packages/file_selector/file_selector_platform_interface/test/assets/hello.txt +++ /dev/null @@ -1 +0,0 @@ -Hello, world! \ No newline at end of file diff --git a/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart b/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart deleted file mode 100644 index 6809cee66963..000000000000 --- a/packages/file_selector/file_selector_platform_interface/test/file_selector_platform_interface_test.dart +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2020 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:mockito/mockito.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; -import 'package:file_selector_platform_interface/src/method_channel/method_channel_file_selector.dart'; - -void main() { - group('$FileSelectorPlatform', () { - test('$MethodChannelFileSelector() is the default instance', () { - expect(FileSelectorPlatform.instance, - isInstanceOf()); - }); - - test('Cannot be implemented with `implements`', () { - expect(() { - FileSelectorPlatform.instance = ImplementsFileSelectorPlatform(); - }, throwsA(isInstanceOf())); - }); - - test('Can be mocked with `implements`', () { - final FileSelectorPlatformMock mock = FileSelectorPlatformMock(); - FileSelectorPlatform.instance = mock; - }); - - test('Can be extended', () { - FileSelectorPlatform.instance = ExtendsFileSelectorPlatform(); - }); - }); -} - -class FileSelectorPlatformMock extends Mock - with MockPlatformInterfaceMixin - implements FileSelectorPlatform {} - -class ImplementsFileSelectorPlatform extends Mock - implements FileSelectorPlatform {} - -class ExtendsFileSelectorPlatform extends FileSelectorPlatform {} diff --git a/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart b/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart deleted file mode 100644 index 99f9fe0f0e3b..000000000000 --- a/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright 2020 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; -import 'package:file_selector_platform_interface/src/method_channel/method_channel_file_selector.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - group('$MethodChannelFileSelector()', () { - MethodChannelFileSelector plugin = MethodChannelFileSelector(); - - final List log = []; - - setUp(() { - plugin.channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - return null; - }); - - log.clear(); - }); - - group('#openFile', () { - test('passes the accepted type groups correctly', () async { - final group = XTypeGroup( - label: 'text', - extensions: ['.txt'], - mimeTypes: ['text/plain'], - macUTIs: ['public.text'], - ); - - final groupTwo = XTypeGroup( - label: 'image', - extensions: ['.jpg'], - mimeTypes: ['image/jpg'], - macUTIs: ['public.image'], - webWildCards: ['image/*']); - - await plugin.openFile(acceptedTypeGroups: [group, groupTwo]); - - expect( - log, - [ - isMethodCall('openFile', arguments: { - 'acceptedTypeGroups': [group.toJSON(), groupTwo.toJSON()], - 'initialDirectory': null, - 'confirmButtonText': null, - 'multiple': false, - }), - ], - ); - }); - test('passes initialDirectory correctly', () async { - await plugin.openFile(initialDirectory: "/example/directory"); - - expect( - log, - [ - isMethodCall('openFile', arguments: { - 'acceptedTypeGroups': null, - 'initialDirectory': "/example/directory", - 'confirmButtonText': null, - 'multiple': false, - }), - ], - ); - }); - test('passes confirmButtonText correctly', () async { - await plugin.openFile(confirmButtonText: "Open File"); - - expect( - log, - [ - isMethodCall('openFile', arguments: { - 'acceptedTypeGroups': null, - 'initialDirectory': null, - 'confirmButtonText': "Open File", - 'multiple': false, - }), - ], - ); - }); - }); - group('#openFiles', () { - test('passes the accepted type groups correctly', () async { - final group = XTypeGroup( - label: 'text', - extensions: ['.txt'], - mimeTypes: ['text/plain'], - macUTIs: ['public.text'], - ); - - final groupTwo = XTypeGroup( - label: 'image', - extensions: ['.jpg'], - mimeTypes: ['image/jpg'], - macUTIs: ['public.image'], - webWildCards: ['image/*']); - - await plugin.openFiles(acceptedTypeGroups: [group, groupTwo]); - - expect( - log, - [ - isMethodCall('openFile', arguments: { - 'acceptedTypeGroups': [group.toJSON(), groupTwo.toJSON()], - 'initialDirectory': null, - 'confirmButtonText': null, - 'multiple': true, - }), - ], - ); - }); - test('passes initialDirectory correctly', () async { - await plugin.openFiles(initialDirectory: "/example/directory"); - - expect( - log, - [ - isMethodCall('openFile', arguments: { - 'acceptedTypeGroups': null, - 'initialDirectory': "/example/directory", - 'confirmButtonText': null, - 'multiple': true, - }), - ], - ); - }); - test('passes confirmButtonText correctly', () async { - await plugin.openFiles(confirmButtonText: "Open File"); - - expect( - log, - [ - isMethodCall('openFile', arguments: { - 'acceptedTypeGroups': null, - 'initialDirectory': null, - 'confirmButtonText': "Open File", - 'multiple': true, - }), - ], - ); - }); - }); - - group('#getSavePath', () { - test('passes the accepted type groups correctly', () async { - final group = XTypeGroup( - label: 'text', - extensions: ['.txt'], - mimeTypes: ['text/plain'], - macUTIs: ['public.text'], - ); - - final groupTwo = XTypeGroup( - label: 'image', - extensions: ['.jpg'], - mimeTypes: ['image/jpg'], - macUTIs: ['public.image'], - webWildCards: ['image/*']); - - await plugin.getSavePath(acceptedTypeGroups: [group, groupTwo]); - - expect( - log, - [ - isMethodCall('getSavePath', arguments: { - 'acceptedTypeGroups': [group.toJSON(), groupTwo.toJSON()], - 'initialDirectory': null, - 'suggestedName': null, - 'confirmButtonText': null, - }), - ], - ); - }); - test('passes initialDirectory correctly', () async { - await plugin.getSavePath(initialDirectory: "/example/directory"); - - expect( - log, - [ - isMethodCall('getSavePath', arguments: { - 'acceptedTypeGroups': null, - 'initialDirectory': "/example/directory", - 'suggestedName': null, - 'confirmButtonText': null, - }), - ], - ); - }); - test('passes confirmButtonText correctly', () async { - await plugin.getSavePath(confirmButtonText: "Open File"); - - expect( - log, - [ - isMethodCall('getSavePath', arguments: { - 'acceptedTypeGroups': null, - 'initialDirectory': null, - 'suggestedName': null, - 'confirmButtonText': "Open File", - }), - ], - ); - }); - group('#getDirectoryPath', () { - test('passes initialDirectory correctly', () async { - await plugin.getDirectoryPath(initialDirectory: "/example/directory"); - - expect( - log, - [ - isMethodCall('getDirectoryPath', arguments: { - 'initialDirectory': "/example/directory", - 'confirmButtonText': null, - }), - ], - ); - }); - test('passes confirmButtonText correctly', () async { - await plugin.getDirectoryPath(confirmButtonText: "Open File"); - - expect( - log, - [ - isMethodCall('getDirectoryPath', arguments: { - 'initialDirectory': null, - 'confirmButtonText': "Open File", - }), - ], - ); - }); - }); - }); - }); -} diff --git a/packages/file_selector/file_selector_platform_interface/test/x_file_html_test.dart b/packages/file_selector/file_selector_platform_interface/test/x_file_html_test.dart deleted file mode 100644 index f888a0486ca7..000000000000 --- a/packages/file_selector/file_selector_platform_interface/test/x_file_html_test.dart +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2020 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -@TestOn('chrome') // Uses web-only Flutter SDK - -import 'dart:convert'; -import 'dart:html' as html; -import 'dart:typed_data'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; - -import 'dart:html'; - -final String expectedStringContents = 'Hello, world!'; -final Uint8List bytes = utf8.encode(expectedStringContents); -final html.File textFile = html.File([bytes], 'hello.txt'); -final String textFileUrl = html.Url.createObjectUrl(textFile); - -void main() { - group('Create with an objectUrl', () { - final file = XFile(textFileUrl); - - test('Can be read as a string', () async { - expect(await file.readAsString(), equals(expectedStringContents)); - }); - test('Can be read as bytes', () async { - expect(await file.readAsBytes(), equals(bytes)); - }); - - test('Can be read as a stream', () async { - expect(await file.openRead().first, equals(bytes)); - }); - - test('Stream can be sliced', () async { - expect(await file.openRead(2, 5).first, equals(bytes.sublist(2, 5))); - }); - }); - - group('Create from data', () { - final file = XFile.fromData(bytes); - - test('Can be read as a string', () async { - expect(await file.readAsString(), equals(expectedStringContents)); - }); - test('Can be read as bytes', () async { - expect(await file.readAsBytes(), equals(bytes)); - }); - - test('Can be read as a stream', () async { - expect(await file.openRead().first, equals(bytes)); - }); - - test('Stream can be sliced', () async { - expect(await file.openRead(2, 5).first, equals(bytes.sublist(2, 5))); - }); - }); - - group('saveTo(..)', () { - final String xFileDomElementId = '__x_file_dom_element'; - - group('XFile saveTo(..)', () { - test('creates a DOM container', () async { - XFile file = XFile.fromData(bytes); - - await file.saveTo(''); - - final container = querySelector('#${xFileDomElementId}'); - - expect(container, isNotNull); - }); - - test('create anchor element', () async { - XFile file = XFile.fromData(bytes, name: textFile.name); - - await file.saveTo('path'); - - final container = querySelector('#${xFileDomElementId}'); - final AnchorElement element = container?.children?.firstWhere( - (element) => element.tagName == 'A', - orElse: () => null); - - expect(element, isNotNull); - expect(element.href, file.path); - expect(element.download, file.name); - }); - - test('anchor element is clicked', () async { - final mockAnchor = AnchorElement(); - - XFileTestOverrides overrides = XFileTestOverrides( - createAnchorElement: (_, __) => mockAnchor, - ); - - XFile file = - XFile.fromData(bytes, name: textFile.name, overrides: overrides); - - bool clicked = false; - mockAnchor.onClick.listen((event) => clicked = true); - - await file.saveTo('path'); - - expect(clicked, true); - }); - }); - }); -} diff --git a/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart b/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart deleted file mode 100644 index b669324462b2..000000000000 --- a/packages/file_selector/file_selector_platform_interface/test/x_file_io_test.dart +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2020 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -@TestOn('vm') // Uses dart:io - -import 'dart:convert'; -import 'dart:io'; -import 'dart:typed_data'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; - -// Please note that executing this test with command -// `flutter test test/x_file_io_test.dart` will set the directory -// to ./file_selector_platform_interface. -// -// This will cause our hello.txt file to be not be found. Please -// execute this test with `flutter test` or change the path prefix -// to ./test/assets/ -// -// https://github.com/flutter/flutter/issues/20907 - -final pathPrefix = './assets/'; -final path = pathPrefix + 'hello.txt'; -final String expectedStringContents = 'Hello, world!'; -final Uint8List bytes = utf8.encode(expectedStringContents); -final File textFile = File(path); -final String textFilePath = textFile.path; - -void main() { - group('Create with a path', () { - final file = XFile(textFilePath); - - test('Can be read as a string', () async { - expect(await file.readAsString(), equals(expectedStringContents)); - }); - test('Can be read as bytes', () async { - expect(await file.readAsBytes(), equals(bytes)); - }); - - test('Can be read as a stream', () async { - expect(await file.openRead().first, equals(bytes)); - }); - - test('Stream can be sliced', () async { - expect(await file.openRead(2, 5).first, equals(bytes.sublist(2, 5))); - }); - - test('saveTo(..) creates file', () async { - File removeBeforeTest = File(pathPrefix + 'newFilePath.txt'); - if (removeBeforeTest.existsSync()) { - await removeBeforeTest.delete(); - } - - await file.saveTo(pathPrefix + 'newFilePath.txt'); - File newFile = File(pathPrefix + 'newFilePath.txt'); - - expect(newFile.existsSync(), isTrue); - expect(newFile.readAsStringSync(), 'Hello, world!'); - - await newFile.delete(); - }); - }); - - group('Create with data', () { - final file = XFile.fromData(bytes); - - test('Can be read as a string', () async { - expect(await file.readAsString(), equals(expectedStringContents)); - }); - test('Can be read as bytes', () async { - expect(await file.readAsBytes(), equals(bytes)); - }); - - test('Can be read as a stream', () async { - expect(await file.openRead().first, equals(bytes)); - }); - - test('Stream can be sliced', () async { - expect(await file.openRead(2, 5).first, equals(bytes.sublist(2, 5))); - }); - - test('Function saveTo(..) creates file', () async { - File removeBeforeTest = File(pathPrefix + 'newFileData.txt'); - if (removeBeforeTest.existsSync()) { - await removeBeforeTest.delete(); - } - - await file.saveTo(pathPrefix + 'newFileData.txt'); - File newFile = File(pathPrefix + 'newFileData.txt'); - - expect(newFile.existsSync(), isTrue); - expect(newFile.readAsStringSync(), 'Hello, world!'); - - await newFile.delete(); - }); - }); -} diff --git a/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart b/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart deleted file mode 100644 index 877e530cb342..000000000000 --- a/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2020 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter_test/flutter_test.dart'; -import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; - -void main() { - group('XTypeGroup', () { - test('fails assertion with no parameters set', () { - expect(() => XTypeGroup(), throwsAssertionError); - }); - - test('toJSON() creates correct map', () { - final label = 'test group'; - final extensions = ['.txt', '.jpg']; - final mimeTypes = ['text/plain']; - final macUTIs = ['public.plain-text']; - final webWildCards = ['image/*']; - - final group = XTypeGroup( - label: label, - extensions: extensions, - mimeTypes: mimeTypes, - macUTIs: macUTIs, - webWildCards: webWildCards, - ); - - final jsonMap = group.toJSON(); - expect(jsonMap['label'], label); - expect(jsonMap['extensions'], extensions); - expect(jsonMap['mimeTypes'], mimeTypes); - expect(jsonMap['macUTIs'], macUTIs); - expect(jsonMap['webWildCards'], webWildCards); - }); - }); -} diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md deleted file mode 100644 index 7be26166a1f2..000000000000 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -# 0.1.0 - -- Initial open-source release. diff --git a/packages/file_selector/file_selector_web/LICENSE b/packages/file_selector/file_selector_web/LICENSE deleted file mode 100644 index 2c91f1438173..000000000000 --- a/packages/file_selector/file_selector_web/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2020 The Flutter Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/packages/file_selector/file_selector_web/README.md b/packages/file_selector/file_selector_web/README.md deleted file mode 100644 index e35424c8d320..000000000000 --- a/packages/file_selector/file_selector_web/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# file_picker_web - -The web implementation of [`file_picker`][1]. - -**Please set your constraint to `file_picker_web: '>=0.1.y+x <2.0.0'`** - -## Backward compatible 1.0.0 version is coming -The plugin has reached a stable API, we guarantee that version `1.0.0` will be backward compatible with `0.1.y+z`. -Please use `file_picker_web: '>=0.1.y+x <2.0.0'` as your dependency constraint to allow a smoother ecosystem migration. -For more details see: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 - -## Usage - -### Import the package -To use this plugin in your Flutter Web app, simply add it as a dependency in -your pubspec alongside the base `file_picker` plugin. - -_(This is only temporary: in the future we hope to make this package an -"endorsed" implementation of `file_picker`, so that it is automatically -included in your Flutter Web app when you depend on `package:file_picker`.)_ - -This is what the above means to your `pubspec.yaml`: - -```yaml -... -dependencies: - ... - file_picker: ^0.1.0 - file_picker_web: ^0.1.0 - ... -``` - -### Use the plugin -Once you have the `file_picker_web` dependency in your pubspec, you should -be able to use `package:file_picker` as normal. - -[1]: ../file_picker/file_picker diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart deleted file mode 100644 index 23845f4c2bfe..000000000000 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ /dev/null @@ -1,197 +0,0 @@ -import 'dart:async'; -import 'dart:html'; - -import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; -import 'package:file_selector_platform_interface/src/web_helpers/web_helpers.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_web_plugins/flutter_web_plugins.dart'; -import 'package:meta/meta.dart'; - -final String _kFileSelectorInputsDomId = '__file_selector_web_file_input'; - -/// The web implementation of [FileSelectorPlatform]. -/// -/// This class implements the `package:file_selector` functionality for the web. -class FileSelectorPlugin extends FileSelectorPlatform { - Element _target; - final FileSelectorPluginTestOverrides _overrides; - - bool get _hasTestOverrides => _overrides != null; - - /// Open file dialog for loading files and return a XFile - @override - Future openFile({ - List acceptedTypeGroups, - String initialDirectory, - String confirmButtonText, - }) async { - return (await _openFileHelper(false, acceptedTypeGroups)).first; - } - - /// Open file dialog for loading files and return a XFile - @override - Future> openFiles({ - List acceptedTypeGroups, - String initialDirectory, - String confirmButtonText, - }) async { - return _openFileHelper(true, acceptedTypeGroups); - } - - @override - Future getSavePath({ - List acceptedTypeGroups, - String initialDirectory, - String suggestedName, - String confirmButtonText, - }) async => - null; - - @override - Future getDirectoryPath({ - String initialDirectory, - String confirmButtonText, - }) async => - null; - - /// Load Helper - Future> _openFileHelper( - bool multiple, List acceptedTypes) { - final acceptedTypeString = _getStringFromFilterGroup(acceptedTypes); - - final FileUploadInputElement element = - createFileInputElement(acceptedTypeString, multiple); - - _target.children.clear(); - addElementToContainerAndClick(_target, element); - - return getFilesWhenReady(element); - } - - /// Default constructor, initializes _target to a DOM element that we can use - /// to host HTML elements. - /// overrides parameter allows for testing to override functions - FileSelectorPlugin({ - @visibleForTesting FileSelectorPluginTestOverrides overrides, - }) : _overrides = overrides { - _target = ensureInitialized(_kFileSelectorInputsDomId); - } - - /// Registers this class as the default instance of [FileSelectorPlatform]. - static void registerWith(Registrar registrar) { - FileSelectorPlatform.instance = FileSelectorPlugin(); - } - - /// Convert list of XTypeGroups to a comma-separated string - String _getStringFromFilterGroup(List acceptedTypes) { - List allTypes = List(); - - for (XTypeGroup group in acceptedTypes ?? []) { - assert( - !((group.extensions == null || group.extensions.isEmpty) && - (group.mimeTypes == null || group.mimeTypes.isEmpty) && - (group.webWildCards == null || group.webWildCards.isEmpty)), - 'At least one of extensions / mimeTypes / webWildCards is required for web.'); - - allTypes.addAll(group.extensions - .map((ext) => ext.isNotEmpty && ext[0] != '.' ? '.' + ext : ext)); - allTypes.addAll(group.mimeTypes ?? []); - allTypes.addAll(group.webWildCards ?? []); - } - return allTypes?.where((e) => e.isNotEmpty)?.join(',') ?? ''; - } - - /// Creates a file input element with only the accept attribute - @visibleForTesting - FileUploadInputElement createFileInputElement( - String accepted, bool multiple) { - if (_hasTestOverrides && _overrides.createFileInputElement != null) { - return _overrides.createFileInputElement(accepted, multiple); - } - - final FileUploadInputElement element = FileUploadInputElement(); - if (accepted.isNotEmpty) { - element.accept = accepted; - } - element.multiple = multiple; - - return element; - } - - List _getXFilesFromFiles(List files) { - List xFiles = List(); - - for (File file in files) { - String url = Url.createObjectUrl(file); - String name = file.name; - int length = file.size; - int modified = file.lastModified; - - DateTime modifiedDate = DateTime.fromMillisecondsSinceEpoch(modified); - - xFiles.add( - XFile(url, name: name, lastModified: modifiedDate, length: length)); - } - - return xFiles; - } - - /// Getter for retrieving files from an input element - @visibleForTesting - List getFilesFromInputElement(InputElement element) { - if (_hasTestOverrides && _overrides.getFilesFromInputElement != null) { - return _overrides.getFilesFromInputElement(element); - } - - return element?.files ?? []; - } - - /// Listen for file input element to change and retrieve files when - /// this happens. - @visibleForTesting - Future> getFilesWhenReady(InputElement element) { - if (_hasTestOverrides && _overrides.getFilesWhenReady != null) { - return _overrides.getFilesWhenReady(element); - } - - final Completer> _completer = Completer(); - - // Listens for element change - element.onChange.first.then((event) { - // File type from dart:html class - final List files = getFilesFromInputElement(element); - - // Create XFile from dart:html Files - final xFiles = _getXFilesFromFiles(files); - - _completer.complete(xFiles); - }); - - element.onError.first.then((event) { - ErrorEvent error = event; - _completer.completeError( - PlatformException(code: error.type, message: error.message)); - }); - - return _completer.future; - } -} - -/// Overrides some functions to allow testing -@visibleForTesting -class FileSelectorPluginTestOverrides { - /// For overriding the creation of the file input element. - Element Function(String accepted, bool multiple) createFileInputElement; - - /// For overriding retrieving a file from the input element. - List Function(InputElement input) getFilesFromInputElement; - - /// For overriding waiting for the files to be ready. Useful for testing so we do not hang here. - Future> Function(InputElement input) getFilesWhenReady; - - /// Constructor for test override class - FileSelectorPluginTestOverrides( - {this.createFileInputElement, - this.getFilesFromInputElement, - this.getFilesWhenReady}); -} diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml deleted file mode 100644 index 742065c4132a..000000000000 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ /dev/null @@ -1,36 +0,0 @@ -name: file_selector_web -description: Web platform implementation of file_selector -homepage: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector -# 0.1.y+z is compatible with 1.0.0, if you land a breaking change bump -# the version to 2.0.0. -# See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.1.0 - -flutter: - plugin: - platforms: - web: - pluginClass: FileSelectorPlugin - fileName: file_selector_web.dart - -dependencies: - file_selector_platform_interface: - path: ../file_selector_platform_interface - platform_detect: ^1.4.0 - flutter: - sdk: flutter - flutter_web_plugins: - sdk: flutter - meta: ^1.1.7 - -dev_dependencies: - flutter_test: - sdk: flutter - pedantic: ^1.8.0 - mockito: ^4.1.1 - integration_test: - path: ../../integration_test - -environment: - sdk: ">=2.2.0 <3.0.0" - flutter: ">=1.10.0 <2.0.0" diff --git a/packages/file_selector/file_selector_web/test/README.md b/packages/file_selector/file_selector_web/test/README.md deleted file mode 100644 index 0a3f392fe3e4..000000000000 --- a/packages/file_selector/file_selector_web/test/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Running browser_tests - -Make sure you have updated to the latest Flutter master. - -1. Check what version of Chrome is running on the machine you're running tests on. - -2. Download and install driver for that version from here: - * - -3. Start the driver using `chromedriver --port=4444` - -4. Change into the `test` directory of your clone. - -5. Run tests: `flutter drive -d web-server --release --browser-name=chrome --target=test_driver/TEST_NAME_integration.dart`. diff --git a/packages/file_selector/file_selector_web/test/lib/main.dart b/packages/file_selector/file_selector_web/test/lib/main.dart deleted file mode 100644 index 29bd9078749c..000000000000 --- a/packages/file_selector/file_selector_web/test/lib/main.dart +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2020 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; - -void main() { - runApp(MyApp()); -} - -/// An app that runs the tests -class MyApp extends StatefulWidget { - @override - MyAppState createState() => MyAppState(); -} - -/// State for MyApp -class MyAppState extends State { - @override - Widget build(BuildContext context) { - return Text('Testing... Look at the console output for results!'); - } -} diff --git a/packages/file_selector/file_selector_web/test/pubspec.yaml b/packages/file_selector/file_selector_web/test/pubspec.yaml deleted file mode 100644 index f5e42e327564..000000000000 --- a/packages/file_selector/file_selector_web/test/pubspec.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: regular_integration_tests -publish_to: none - -environment: - sdk: ">=2.2.2 <3.0.0" - -dependencies: - flutter: - sdk: flutter - -dev_dependencies: - file_selector_web: - path: ../ - file_selector_platform_interface: - path: ../../file_selector_platform_interface - google_maps: ^3.4.4 - flutter_driver: - sdk: flutter - flutter_test: - sdk: flutter - integration_test: - path: ../../../integration_test - http: ^0.12.2 - mockito: ^4.1.1 \ No newline at end of file diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_integration.dart b/packages/file_selector/file_selector_web/test/test_driver/web_integration.dart deleted file mode 100644 index 4b44f09adf30..000000000000 --- a/packages/file_selector/file_selector_web/test/test_driver/web_integration.dart +++ /dev/null @@ -1,269 +0,0 @@ -import 'dart:async'; - -import 'package:integration_test/integration_test.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'dart:convert'; -import 'dart:typed_data'; -import 'dart:html'; - -import 'package:file_selector_web/file_selector_web.dart'; -import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; - -final String domElementId = '__file_selector_web_file_input'; -final String xFileDomElementId = '__x_file_dom_element'; - -final textGroup = XTypeGroup( - label: 'Text Files', - extensions: ['txt', 'json'], - mimeTypes: ['plain/text', 'application/json'], -); - -final badGroup = XTypeGroup( - label: 'Non-Web Group', - macUTIs: ['fake-uti'], -); - -final String expectedStringContents = 'Hello, world!'; -final Uint8List bytes = utf8.encode(expectedStringContents); -final File textFile = File([bytes], 'hello.txt'); - -final String expectedStringContents2 = 'This is the other test file'; -final Uint8List bytes2 = utf8.encode(expectedStringContents2); -final File textFile2 = File([bytes2], 'test2.txt'); - -/// Test Markers -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - print( - "Note that the following message can be ignored in the test output:\n'File chooser dialog can only be shown with a user activation'"); - - FileSelectorPlugin plugin; - - group('openFile(..)', () { - testWidgets('creates a DOM container', (WidgetTester tester) async { - plugin = FileSelectorPlugin(); - - final result = querySelector('#${domElementId}'); - expect(result, isNotNull); - }); - - testWidgets('creates correct input element', (WidgetTester tester) async { - final overrides = FileSelectorPluginTestOverrides( - getFilesWhenReady: (_) => Future.value([XFile('path')]), - ); - - plugin = FileSelectorPlugin( - overrides: overrides, - ); - - final container = querySelector('#${domElementId}'); - - final file = await plugin.openFile(acceptedTypeGroups: [textGroup]); - - expect(file, isNotNull); - - final result = container?.children?.firstWhere( - (element) => element.tagName == 'INPUT', - orElse: () => null); - - expect(result, isNotNull); - expect(result.getAttribute('type'), 'file'); - expect(result.getAttribute('accept'), - '.txt,.json,plain/text,application/json'); - }); - - testWidgets('input element is clicked', (WidgetTester tester) async { - final mockInput = FileUploadInputElement(); - - final overrides = FileSelectorPluginTestOverrides( - getFilesWhenReady: (_) => Future.value([XFile('path')]), - createFileInputElement: (_, __) => mockInput, - ); - - plugin = FileSelectorPlugin( - overrides: overrides, - ); - - bool clicked = false; - mockInput.onClick.listen((event) => clicked = true); - - await plugin.openFile(acceptedTypeGroups: [textGroup]); - - expect(clicked, true); - }); - - testWidgets('get XFile from input element', (WidgetTester tester) async { - final mockInput = FileUploadInputElement(); - - final overrides = FileSelectorPluginTestOverrides( - getFilesFromInputElement: (_) => [textFile], - createFileInputElement: (_, __) => mockInput, - ); - - plugin = FileSelectorPlugin( - overrides: overrides, - ); - - // Call load file - final file = plugin.openFile(); - - // Mock selection of a file - mockInput.dispatchEvent(Event('change')); - - // Expect the file to complete - expect(file, completes); - - // Expect that we can read from the file - final loadedFile = await file; - final contents = await loadedFile.readAsString(); - expect(contents, expectedStringContents); - expect(loadedFile.name, textFile.name); - }); - }); - - group('openFiles(..)', () { - testWidgets('creates correct input element', (WidgetTester tester) async { - final overrides = FileSelectorPluginTestOverrides( - getFilesWhenReady: (_) => Future.value([XFile('path'), XFile('path2')]), - ); - - plugin = FileSelectorPlugin( - overrides: overrides, - ); - - final container = querySelector('#${domElementId}'); - - final files = await plugin.openFiles(acceptedTypeGroups: [textGroup]); - - expect(files, isNotNull); - - final FileUploadInputElement result = container?.children?.firstWhere( - (element) => element.tagName == 'INPUT', - orElse: () => null); - - print(result.getAttribute('accept')); - - expect(result, isNotNull); - expect(result.getAttribute('type'), 'file'); - expect(result.getAttribute('accept'), - '.txt,.json,plain/text,application/json'); - expect(result.multiple, true); - }); - - testWidgets('input element is clicked', (WidgetTester tester) async { - final mockInput = FileUploadInputElement(); - - final overrides = FileSelectorPluginTestOverrides( - getFilesWhenReady: (_) => Future.value([XFile('path')]), - createFileInputElement: (_, __) => mockInput, - ); - - plugin = FileSelectorPlugin( - overrides: overrides, - ); - - bool clicked = false; - mockInput.onClick.listen((event) => clicked = true); - - await plugin.openFiles(acceptedTypeGroups: [textGroup]); - - expect(clicked, true); - }); - - testWidgets('get XFiles from input element', (WidgetTester tester) async { - final mockInput = FileUploadInputElement(); - - final overrides = FileSelectorPluginTestOverrides( - getFilesFromInputElement: (_) => [textFile, textFile2], - createFileInputElement: (_, __) => mockInput, - ); - - plugin = FileSelectorPlugin( - overrides: overrides, - ); - - // Call load file - final files = plugin.openFiles(); - - // Mock selection of files - mockInput.dispatchEvent(Event('change')); - - // Expect the file to complete - expect(files, completes); - - // Expect that we can read from the file - final loadedFiles = await files; - final loadedFile1 = loadedFiles[0]; - final loadedFile2 = loadedFiles[1]; - - final contents = await loadedFile1.readAsString(); - expect(contents, expectedStringContents); - expect(loadedFile1.name, textFile.name); - - final contents2 = await loadedFile2.readAsString(); - expect(contents2, expectedStringContents2); - expect(loadedFile2.name, textFile2.name); - }); - }); - - testWidgets('getSavePath completes', (WidgetTester tester) async { - plugin = FileSelectorPlugin(); - final path = plugin.getSavePath(); - expect(path, completes); - }); - - testWidgets('getDirectoryPath completes', (WidgetTester tester) async { - plugin = FileSelectorPlugin(); - final path = plugin.getDirectoryPath(); - expect(path, completes); - }); - - group('XFile saveTo(..)', () { - testWidgets('creates a DOM container', (WidgetTester tester) async { - XFile file = XFile.fromData(bytes); - - await file.saveTo(''); - - final container = querySelector('#${xFileDomElementId}'); - - expect(container, isNotNull); - }); - - testWidgets('create anchor element', (WidgetTester tester) async { - XFile file = XFile.fromData(bytes, name: textFile.name); - - await file.saveTo('path'); - - final container = querySelector('#${xFileDomElementId}'); - final AnchorElement element = container?.children - ?.firstWhere((element) => element.tagName == 'A', orElse: () => null); - - expect(element, isNotNull); - expect(element.href, file.path); - expect(element.download, file.name); - }); - - testWidgets('anchor element is clicked', (WidgetTester tester) async { - final mockAnchor = AnchorElement(); - - XFileTestOverrides overrides = XFileTestOverrides( - createAnchorElement: (_, __) => mockAnchor, - ); - - XFile file = - XFile.fromData(bytes, name: textFile.name, overrides: overrides); - - bool clicked = false; - mockAnchor.onClick.listen((event) => clicked = true); - - await file.saveTo('path'); - - expect(clicked, true); - }); - }); - - print( - "Note that the following message can be ignored in the test output:\n'File chooser dialog can only be shown with a user activation'"); -} diff --git a/packages/file_selector/file_selector_web/test/test_driver/web_integration_test.dart b/packages/file_selector/file_selector_web/test/test_driver/web_integration_test.dart deleted file mode 100644 index 1c5d1fcbcca7..000000000000 --- a/packages/file_selector/file_selector_web/test/test_driver/web_integration_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2020 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:integration_test/integration_test_driver.dart'; - -Future main() async => integrationDriver(); diff --git a/packages/file_selector/file_selector_web/test/web/index.html b/packages/file_selector/file_selector_web/test/web/index.html deleted file mode 100644 index b7d4fb222a25..000000000000 --- a/packages/file_selector/file_selector_web/test/web/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Browser Tests - - - - - - From 11a4d74d737314f0c708da5d61c2bd89ae680ef3 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Sun, 11 Oct 2020 18:58:16 -0500 Subject: [PATCH 131/151] Removes file_selector example folder. --- .../file_selector/example/.gitignore | 48 --- .../file_selector/example/.metadata | 10 - .../file_selector/example/README.md | 16 - .../file_selector/example/lib/main.dart | 287 ------------------ .../file_selector/example/pubspec.yaml | 78 ----- .../example/test/widget_test.dart | 8 - .../file_selector/example/web/favicon.png | Bin 917 -> 0 bytes .../example/web/icons/Icon-192.png | Bin 5292 -> 0 bytes .../example/web/icons/Icon-512.png | Bin 8252 -> 0 bytes .../file_selector/example/web/index.html | 33 -- .../file_selector/example/web/manifest.json | 23 -- 11 files changed, 503 deletions(-) delete mode 100644 packages/file_selector/file_selector/example/.gitignore delete mode 100644 packages/file_selector/file_selector/example/.metadata delete mode 100644 packages/file_selector/file_selector/example/README.md delete mode 100644 packages/file_selector/file_selector/example/lib/main.dart delete mode 100644 packages/file_selector/file_selector/example/pubspec.yaml delete mode 100644 packages/file_selector/file_selector/example/test/widget_test.dart delete mode 100644 packages/file_selector/file_selector/example/web/favicon.png delete mode 100644 packages/file_selector/file_selector/example/web/icons/Icon-192.png delete mode 100644 packages/file_selector/file_selector/example/web/icons/Icon-512.png delete mode 100644 packages/file_selector/file_selector/example/web/index.html delete mode 100644 packages/file_selector/file_selector/example/web/manifest.json diff --git a/packages/file_selector/file_selector/example/.gitignore b/packages/file_selector/file_selector/example/.gitignore deleted file mode 100644 index 7abd0753cfc3..000000000000 --- a/packages/file_selector/file_selector/example/.gitignore +++ /dev/null @@ -1,48 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.pub-cache/ -.pub/ -/build/ - -# Web related -lib/generated_plugin_registrant.dart - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json - -# Currently only web supported -android/ -ios/ - -# Exceptions to above rules. -!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/packages/file_selector/file_selector/example/.metadata b/packages/file_selector/file_selector/example/.metadata deleted file mode 100644 index 897381f2373f..000000000000 --- a/packages/file_selector/file_selector/example/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: 7736f3bc90270dcb0480db2ccffbf1d13c28db85 - channel: dev - -project_type: app diff --git a/packages/file_selector/file_selector/example/README.md b/packages/file_selector/file_selector/example/README.md deleted file mode 100644 index a13562602822..000000000000 --- a/packages/file_selector/file_selector/example/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# 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. diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart deleted file mode 100644 index 0a383ded9728..000000000000 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ /dev/null @@ -1,287 +0,0 @@ -import 'dart:typed_data'; - -import 'package:flutter/material.dart'; -import 'package:file_selector/file_selector.dart'; - -void main() { - runApp(MyApp()); -} - -/// MyApp is the Main Application -class MyApp extends StatelessWidget { - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'File Selector Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - visualDensity: VisualDensity.adaptivePlatformDensity, - ), - home: MyHomePage(title: 'File Selector Demo Home Page'), - routes: { - '/save': (context) => SaveTest(), - '/open': (context) => OpenTest(), - }, - ); - } -} - -/// Page for showing an example of saving with file_selector -class SaveTest extends StatefulWidget { - /// Constructor for the SaveTest page - SaveTest({Key key}) : super(key: key); - - @override - _SaveTestState createState() => _SaveTestState(); -} - -class _SaveTestState extends State { - final TextEditingController _fileController = TextEditingController(); - final TextEditingController _nameController = TextEditingController(); - - @override - void dispose() { - _fileController.dispose(); - super.dispose(); - } - - void _saveFile() async { - final path = await getSavePath(); - - final data = Uint8List.fromList(_fileController.text.codeUnits); - - final new_file = (_nameController.text == '') - ? XFile.fromData(data, mimeType: 'text/plain') - : XFile.fromData(data, - mimeType: 'text/plain', name: _nameController.text); - - new_file.saveTo(path); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text("Save Example"), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: 300, - child: TextField( - minLines: 1, - maxLines: 12, - controller: _nameController, - decoration: InputDecoration( - hintText: '(Optional) Suggest File Name', - ), - ), - ), - Container( - width: 300, - child: TextField( - minLines: 1, - maxLines: 12, - controller: _fileController, - decoration: InputDecoration( - hintText: 'Enter File Contents', - ), - ), - ), - SizedBox(height: 10), - RaisedButton( - child: Text('Press to save a text file'), - onPressed: () => {_saveFile()}, - ), - ], - ), - ), - ); - } -} - -/// Screen that shows an example of openFile(s) -class OpenTest extends StatefulWidget { - /// Default constructor - OpenTest({Key key}) : super(key: key); - - @override - _OpenTestState createState() => _OpenTestState(); -} - -class _OpenTestState extends State { - void _onOpenImageFile() async { - final typeGroup = XTypeGroup(label: 'images', extensions: ['jpg', 'png']); - - final file = await openFile(acceptedTypeGroups: [typeGroup]); - - await showDialog( - context: context, - builder: (context) { - return ImageDisplay(file: file); - }); - } - - void _onOpenTextFile() async { - final typeGroup = XTypeGroup(label: 'images', extensions: ['txt', 'json']); - - final file = await openFile(acceptedTypeGroups: [typeGroup]); - - await showDialog( - context: context, - builder: (context) { - return TextDisplay(file: file); - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text("Open Example"), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - RaisedButton( - child: Text('Press to open an image file(png, jpg)'), - onPressed: () => _onOpenImageFile(), - ), - RaisedButton( - child: Text('Press to open a text file (json, txt)'), - onPressed: () => _onOpenTextFile(), - ), - ], - ), - ), - ); - } -} - -/// Widget that displays a text file in a dialog -class TextDisplay extends StatefulWidget { - /// File to display - final XFile file; - - /// Default Constructor - TextDisplay({Key key, @required this.file}) : super(key: key); - - @override - _TextDisplayState createState() => _TextDisplayState(); -} - -class _TextDisplayState extends State { - String fileContents; - - @override - void initState() { - super.initState(); - _getFileContents(); - } - - void _getFileContents() async { - String contents = await widget.file.readAsString(); - setState(() => fileContents = contents); - } - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: Text(widget.file.name), - content: Scrollbar( - child: SingleChildScrollView( - child: Text( - fileContents ?? - 'Opening file contents...\nThis may take a while if your file is large.', - ), - ), - ), - actions: [ - FlatButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ); - } -} - -/// Widget that displays a text file in a dialog -class ImageDisplay extends StatefulWidget { - /// File to display - final XFile file; - - /// Default Constructor - ImageDisplay({Key key, @required this.file}) : super(key: key); - - @override - _ImageDisplayState createState() => _ImageDisplayState(); -} - -class _ImageDisplayState extends State { - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: Text(widget.file.name), - content: Image.network(widget.file.path), - actions: [ - FlatButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ); - } -} - -/// Home Page of the application -class MyHomePage extends StatefulWidget { - /// Constructor for MyHomePage - MyHomePage({Key key, this.title}) : super(key: key); - - /// Title of Home Page - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -/// State of Home Page -class _MyHomePageState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox(height: 10), - RaisedButton( - child: Text('Press to try saving a file'), - onPressed: () => Navigator.pushNamed(context, '/save'), - ), - RaisedButton( - child: Text('Press to try opening a file'), - onPressed: () => Navigator.pushNamed(context, '/open'), - ), - ], - ), - ), - ); - } -} diff --git a/packages/file_selector/file_selector/example/pubspec.yaml b/packages/file_selector/file_selector/example/pubspec.yaml deleted file mode 100644 index 58f0abbf2658..000000000000 --- a/packages/file_selector/file_selector/example/pubspec.yaml +++ /dev/null @@ -1,78 +0,0 @@ -name: example -description: A new Flutter project. - -# The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+1 - -environment: - sdk: ">=2.7.0 <3.0.0" - -dependencies: - flutter: - sdk: flutter - - file_selector: - path: ../ - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.3 - -dev_dependencies: - flutter_test: - sdk: flutter - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. -flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, 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 from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/file_selector/file_selector/example/test/widget_test.dart b/packages/file_selector/file_selector/example/test/widget_test.dart deleted file mode 100644 index 570e0e4768dc..000000000000 --- a/packages/file_selector/file_selector/example/test/widget_test.dart +++ /dev/null @@ -1,8 +0,0 @@ -// 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. - -void main() {} diff --git a/packages/file_selector/file_selector/example/web/favicon.png b/packages/file_selector/file_selector/example/web/favicon.png deleted file mode 100644 index 8aaa46ac1ae21512746f852a42ba87e4165dfdd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM diff --git a/packages/file_selector/file_selector/example/web/icons/Icon-192.png b/packages/file_selector/file_selector/example/web/icons/Icon-192.png deleted file mode 100644 index b749bfef07473333cf1dd31e9eed89862a5d52aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5292 zcmZ`-2T+sGz6~)*FVZ`aW+(v>MIm&M-g^@e2u-B-DoB?qO+b1Tq<5uCCv>ESfRum& zp%X;f!~1{tzL__3=gjVJ=j=J>+nMj%ncXj1Q(b|Ckbw{Y0FWpt%4y%$uD=Z*c-x~o zE;IoE;xa#7Ll5nj-e4CuXB&G*IM~D21rCP$*xLXAK8rIMCSHuSu%bL&S3)8YI~vyp@KBu9Ph7R_pvKQ@xv>NQ`dZp(u{Z8K3yOB zn7-AR+d2JkW)KiGx0hosml;+eCXp6+w%@STjFY*CJ?udJ64&{BCbuebcuH;}(($@@ znNlgBA@ZXB)mcl9nbX#F!f_5Z=W>0kh|UVWnf!At4V*LQP%*gPdCXd6P@J4Td;!Ur z<2ZLmwr(NG`u#gDEMP19UcSzRTL@HsK+PnIXbVBT@oHm53DZr?~V(0{rsalAfwgo zEh=GviaqkF;}F_5-yA!1u3!gxaR&Mj)hLuj5Q-N-@Lra{%<4ONja8pycD90&>yMB` zchhd>0CsH`^|&TstH-8+R`CfoWqmTTF_0?zDOY`E`b)cVi!$4xA@oO;SyOjJyP^_j zx^@Gdf+w|FW@DMdOi8=4+LJl$#@R&&=UM`)G!y%6ZzQLoSL%*KE8IO0~&5XYR9 z&N)?goEiWA(YoRfT{06&D6Yuu@Qt&XVbuW@COb;>SP9~aRc+z`m`80pB2o%`#{xD@ zI3RAlukL5L>px6b?QW1Ac_0>ew%NM!XB2(H+1Y3AJC?C?O`GGs`331Nd4ZvG~bMo{lh~GeL zSL|tT*fF-HXxXYtfu5z+T5Mx9OdP7J4g%@oeC2FaWO1D{=NvL|DNZ}GO?O3`+H*SI z=grGv=7dL{+oY0eJFGO!Qe(e2F?CHW(i!!XkGo2tUvsQ)I9ev`H&=;`N%Z{L zO?vV%rDv$y(@1Yj@xfr7Kzr<~0{^T8wM80xf7IGQF_S-2c0)0D6b0~yD7BsCy+(zL z#N~%&e4iAwi4F$&dI7x6cE|B{f@lY5epaDh=2-(4N05VO~A zQT3hanGy_&p+7Fb^I#ewGsjyCEUmSCaP6JDB*=_()FgQ(-pZ28-{qx~2foO4%pM9e z*_63RT8XjgiaWY|*xydf;8MKLd{HnfZ2kM%iq}fstImB-K6A79B~YoPVa@tYN@T_$ zea+9)<%?=Fl!kd(Y!G(-o}ko28hg2!MR-o5BEa_72uj7Mrc&{lRh3u2%Y=Xk9^-qa zBPWaD=2qcuJ&@Tf6ue&)4_V*45=zWk@Z}Q?f5)*z)-+E|-yC4fs5CE6L_PH3=zI8p z*Z3!it{1e5_^(sF*v=0{`U9C741&lub89gdhKp|Y8CeC{_{wYK-LSbp{h)b~9^j!s z7e?Y{Z3pZv0J)(VL=g>l;<}xk=T*O5YR|hg0eg4u98f2IrA-MY+StQIuK-(*J6TRR z|IM(%uI~?`wsfyO6Tgmsy1b3a)j6M&-jgUjVg+mP*oTKdHg?5E`!r`7AE_#?Fc)&a z08KCq>Gc=ne{PCbRvs6gVW|tKdcE1#7C4e`M|j$C5EYZ~Y=jUtc zj`+?p4ba3uy7><7wIokM79jPza``{Lx0)zGWg;FW1^NKY+GpEi=rHJ+fVRGfXO zPHV52k?jxei_!YYAw1HIz}y8ZMwdZqU%ESwMn7~t zdI5%B;U7RF=jzRz^NuY9nM)&<%M>x>0(e$GpU9th%rHiZsIT>_qp%V~ILlyt^V`=d z!1+DX@ah?RnB$X!0xpTA0}lN@9V-ePx>wQ?-xrJr^qDlw?#O(RsXeAvM%}rg0NT#t z!CsT;-vB=B87ShG`GwO;OEbeL;a}LIu=&@9cb~Rsx(ZPNQ!NT7H{@j0e(DiLea>QD zPmpe90gEKHEZ8oQ@6%E7k-Ptn#z)b9NbD@_GTxEhbS+}Bb74WUaRy{w;E|MgDAvHw zL)ycgM7mB?XVh^OzbC?LKFMotw3r@i&VdUV%^Efdib)3@soX%vWCbnOyt@Y4swW925@bt45y0HY3YI~BnnzZYrinFy;L?2D3BAL`UQ zEj))+f>H7~g8*VuWQ83EtGcx`hun$QvuurSMg3l4IP8Fe`#C|N6mbYJ=n;+}EQm;< z!!N=5j1aAr_uEnnzrEV%_E|JpTb#1p1*}5!Ce!R@d$EtMR~%9# zd;h8=QGT)KMW2IKu_fA_>p_und#-;Q)p%%l0XZOXQicfX8M~7?8}@U^ihu;mizj)t zgV7wk%n-UOb z#!P5q?Ex+*Kx@*p`o$q8FWL*E^$&1*!gpv?Za$YO~{BHeGY*5%4HXUKa_A~~^d z=E*gf6&+LFF^`j4$T~dR)%{I)T?>@Ma?D!gi9I^HqvjPc3-v~=qpX1Mne@*rzT&Xw zQ9DXsSV@PqpEJO-g4A&L{F&;K6W60D!_vs?Vx!?w27XbEuJJP&);)^+VF1nHqHBWu z^>kI$M9yfOY8~|hZ9WB!q-9u&mKhEcRjlf2nm_@s;0D#c|@ED7NZE% zzR;>P5B{o4fzlfsn3CkBK&`OSb-YNrqx@N#4CK!>bQ(V(D#9|l!e9(%sz~PYk@8zt zPN9oK78&-IL_F zhsk1$6p;GqFbtB^ZHHP+cjMvA0(LqlskbdYE_rda>gvQLTiqOQ1~*7lg%z*&p`Ry& zRcG^DbbPj_jOKHTr8uk^15Boj6>hA2S-QY(W-6!FIq8h$<>MI>PYYRenQDBamO#Fv zAH5&ImqKBDn0v5kb|8i0wFhUBJTpT!rB-`zK)^SNnRmLraZcPYK7b{I@+}wXVdW-{Ps17qdRA3JatEd?rPV z4@}(DAMf5EqXCr4-B+~H1P#;t@O}B)tIJ(W6$LrK&0plTmnPpb1TKn3?f?Kk``?D+ zQ!MFqOX7JbsXfQrz`-M@hq7xlfNz;_B{^wbpG8des56x(Q)H)5eLeDwCrVR}hzr~= zM{yXR6IM?kXxauLza#@#u?Y|o;904HCqF<8yT~~c-xyRc0-vxofnxG^(x%>bj5r}N zyFT+xnn-?B`ohA>{+ZZQem=*Xpqz{=j8i2TAC#x-m;;mo{{sLB_z(UoAqD=A#*juZ zCv=J~i*O8;F}A^Wf#+zx;~3B{57xtoxC&j^ie^?**T`WT2OPRtC`xj~+3Kprn=rVM zVJ|h5ux%S{dO}!mq93}P+h36mZ5aZg1-?vhL$ke1d52qIiXSE(llCr5i=QUS?LIjc zV$4q=-)aaR4wsrQv}^shL5u%6;`uiSEs<1nG^?$kl$^6DL z43CjY`M*p}ew}}3rXc7Xck@k41jx}c;NgEIhKZ*jsBRZUP-x2cm;F1<5$jefl|ppO zmZd%%?gMJ^g9=RZ^#8Mf5aWNVhjAS^|DQO+q$)oeob_&ZLFL(zur$)); zU19yRm)z<4&4-M}7!9+^Wl}Uk?`S$#V2%pQ*SIH5KI-mn%i;Z7-)m$mN9CnI$G7?# zo`zVrUwoSL&_dJ92YhX5TKqaRkfPgC4=Q&=K+;_aDs&OU0&{WFH}kKX6uNQC6%oUH z2DZa1s3%Vtk|bglbxep-w)PbFG!J17`<$g8lVhqD2w;Z0zGsh-r zxZ13G$G<48leNqR!DCVt9)@}(zMI5w6Wo=N zpP1*3DI;~h2WDWgcKn*f!+ORD)f$DZFwgKBafEZmeXQMAsq9sxP9A)7zOYnkHT9JU zRA`umgmP9d6=PHmFIgx=0$(sjb>+0CHG)K@cPG{IxaJ&Ueo8)0RWgV9+gO7+Bl1(F z7!BslJ2MP*PWJ;x)QXbR$6jEr5q3 z(3}F@YO_P1NyTdEXRLU6fp?9V2-S=E+YaeLL{Y)W%6`k7$(EW8EZSA*(+;e5@jgD^I zaJQ2|oCM1n!A&-8`;#RDcZyk*+RPkn_r8?Ak@agHiSp*qFNX)&i21HE?yuZ;-C<3C zwJGd1lx5UzViP7sZJ&|LqH*mryb}y|%AOw+v)yc`qM)03qyyrqhX?ub`Cjwx2PrR! z)_z>5*!*$x1=Qa-0uE7jy0z`>|Ni#X+uV|%_81F7)b+nf%iz=`fF4g5UfHS_?PHbr zB;0$bK@=di?f`dS(j{l3-tSCfp~zUuva+=EWxJcRfp(<$@vd(GigM&~vaYZ0c#BTs z3ijkxMl=vw5AS&DcXQ%eeKt!uKvh2l3W?&3=dBHU=Gz?O!40S&&~ei2vg**c$o;i89~6DVns zG>9a*`k5)NI9|?W!@9>rzJ;9EJ=YlJTx1r1BA?H`LWijk(rTax9(OAu;q4_wTj-yj z1%W4GW&K4T=uEGb+E!>W0SD_C0RR91 diff --git a/packages/file_selector/file_selector/example/web/icons/Icon-512.png b/packages/file_selector/file_selector/example/web/icons/Icon-512.png deleted file mode 100644 index 88cfd48dff1169879ba46840804b412fe02fefd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8252 zcmd5=2T+s!lYZ%-(h(2@5fr2dC?F^$C=i-}R6$UX8af(!je;W5yC_|HmujSgN*6?W z3knF*TL1$|?oD*=zPbBVex*RUIKsL<(&Rj9%^UD2IK3W?2j>D?eWQgvS-HLymHo9%~|N2Q{~j za?*X-{b9JRowv_*Mh|;*-kPFn>PI;r<#kFaxFqbn?aq|PduQg=2Q;~Qc}#z)_T%x9 zE|0!a70`58wjREmAH38H1)#gof)U3g9FZ^ zF7&-0^Hy{4XHWLoC*hOG(dg~2g6&?-wqcpf{ z&3=o8vw7lMi22jCG9RQbv8H}`+}9^zSk`nlR8?Z&G2dlDy$4#+WOlg;VHqzuE=fM@ z?OI6HEJH4&tA?FVG}9>jAnq_^tlw8NbjNhfqk2rQr?h(F&WiKy03Sn=-;ZJRh~JrD zbt)zLbnabttEZ>zUiu`N*u4sfQaLE8-WDn@tHp50uD(^r-}UsUUu)`!Rl1PozAc!a z?uj|2QDQ%oV-jxUJmJycySBINSKdX{kDYRS=+`HgR2GO19fg&lZKyBFbbXhQV~v~L za^U944F1_GtuFXtvDdDNDvp<`fqy);>Vw=ncy!NB85Tw{&sT5&Ox%-p%8fTS;OzlRBwErvO+ROe?{%q-Zge=%Up|D4L#>4K@Ke=x%?*^_^P*KD zgXueMiS63!sEw@fNLB-i^F|@Oib+S4bcy{eu&e}Xvb^(mA!=U=Xr3||IpV~3K zQWzEsUeX_qBe6fky#M zzOJm5b+l;~>=sdp%i}}0h zO?B?i*W;Ndn02Y0GUUPxERG`3Bjtj!NroLoYtyVdLtl?SE*CYpf4|_${ku2s`*_)k zN=a}V8_2R5QANlxsq!1BkT6$4>9=-Ix4As@FSS;1q^#TXPrBsw>hJ}$jZ{kUHoP+H zvoYiR39gX}2OHIBYCa~6ERRPJ#V}RIIZakUmuIoLF*{sO8rAUEB9|+A#C|@kw5>u0 zBd=F!4I)Be8ycH*)X1-VPiZ+Ts8_GB;YW&ZFFUo|Sw|x~ZajLsp+_3gv((Q#N>?Jz zFBf`~p_#^${zhPIIJY~yo!7$-xi2LK%3&RkFg}Ax)3+dFCjGgKv^1;lUzQlPo^E{K zmCnrwJ)NuSaJEmueEPO@(_6h3f5mFffhkU9r8A8(JC5eOkux{gPmx_$Uv&|hyj)gN zd>JP8l2U&81@1Hc>#*su2xd{)T`Yw< zN$dSLUN}dfx)Fu`NcY}TuZ)SdviT{JHaiYgP4~@`x{&h*Hd>c3K_To9BnQi@;tuoL z%PYQo&{|IsM)_>BrF1oB~+`2_uZQ48z9!)mtUR zdfKE+b*w8cPu;F6RYJiYyV;PRBbThqHBEu_(U{(gGtjM}Zi$pL8Whx}<JwE3RM0F8x7%!!s)UJVq|TVd#hf1zVLya$;mYp(^oZQ2>=ZXU1c$}f zm|7kfk>=4KoQoQ!2&SOW5|JP1)%#55C$M(u4%SP~tHa&M+=;YsW=v(Old9L3(j)`u z2?#fK&1vtS?G6aOt@E`gZ9*qCmyvc>Ma@Q8^I4y~f3gs7*d=ATlP>1S zyF=k&6p2;7dn^8?+!wZO5r~B+;@KXFEn^&C=6ma1J7Au6y29iMIxd7#iW%=iUzq&C=$aPLa^Q zncia$@TIy6UT@69=nbty5epP>*fVW@5qbUcb2~Gg75dNd{COFLdiz3}kODn^U*=@E z0*$7u7Rl2u)=%fk4m8EK1ctR!6%Ve`e!O20L$0LkM#f+)n9h^dn{n`T*^~d+l*Qlx z$;JC0P9+en2Wlxjwq#z^a6pdnD6fJM!GV7_%8%c)kc5LZs_G^qvw)&J#6WSp< zmsd~1-(GrgjC56Pdf6#!dt^y8Rg}!#UXf)W%~PeU+kU`FeSZHk)%sFv++#Dujk-~m zFHvVJC}UBn2jN& zs!@nZ?e(iyZPNo`p1i#~wsv9l@#Z|ag3JR>0#u1iW9M1RK1iF6-RbJ4KYg?B`dET9 zyR~DjZ>%_vWYm*Z9_+^~hJ_|SNTzBKx=U0l9 z9x(J96b{`R)UVQ$I`wTJ@$_}`)_DyUNOso6=WOmQKI1e`oyYy1C&%AQU<0-`(ow)1 zT}gYdwWdm4wW6|K)LcfMe&psE0XGhMy&xS`@vLi|1#Za{D6l@#D!?nW87wcscUZgELT{Cz**^;Zb~7 z(~WFRO`~!WvyZAW-8v!6n&j*PLm9NlN}BuUN}@E^TX*4Or#dMMF?V9KBeLSiLO4?B zcE3WNIa-H{ThrlCoN=XjOGk1dT=xwwrmt<1a)mrRzg{35`@C!T?&_;Q4Ce=5=>z^*zE_c(0*vWo2_#TD<2)pLXV$FlwP}Ik74IdDQU@yhkCr5h zn5aa>B7PWy5NQ!vf7@p_qtC*{dZ8zLS;JetPkHi>IvPjtJ#ThGQD|Lq#@vE2xdl%`x4A8xOln}BiQ92Po zW;0%A?I5CQ_O`@Ad=`2BLPPbBuPUp@Hb%a_OOI}y{Rwa<#h z5^6M}s7VzE)2&I*33pA>e71d78QpF>sNK;?lj^Kl#wU7G++`N_oL4QPd-iPqBhhs| z(uVM}$ItF-onXuuXO}o$t)emBO3Hjfyil@*+GF;9j?`&67GBM;TGkLHi>@)rkS4Nj zAEk;u)`jc4C$qN6WV2dVd#q}2X6nKt&X*}I@jP%Srs%%DS92lpDY^K*Sx4`l;aql$ zt*-V{U&$DM>pdO?%jt$t=vg5|p+Rw?SPaLW zB6nvZ69$ne4Z(s$3=Rf&RX8L9PWMV*S0@R zuIk&ba#s6sxVZ51^4Kon46X^9`?DC9mEhWB3f+o4#2EXFqy0(UTc>GU| zGCJmI|Dn-dX#7|_6(fT)>&YQ0H&&JX3cTvAq(a@ydM4>5Njnuere{J8p;3?1az60* z$1E7Yyxt^ytULeokgDnRVKQw9vzHg1>X@@jM$n$HBlveIrKP5-GJq%iWH#odVwV6cF^kKX(@#%%uQVb>#T6L^mC@)%SMd4DF? zVky!~ge27>cpUP1Vi}Z32lbLV+CQy+T5Wdmva6Fg^lKb!zrg|HPU=5Qu}k;4GVH+x z%;&pN1LOce0w@9i1Mo-Y|7|z}fbch@BPp2{&R-5{GLoeu8@limQmFF zaJRR|^;kW_nw~0V^ zfTnR!Ni*;-%oSHG1yItARs~uxra|O?YJxBzLjpeE-=~TO3Dn`JL5Gz;F~O1u3|FE- zvK2Vve`ylc`a}G`gpHg58Cqc9fMoy1L}7x7T>%~b&irrNMo?np3`q;d3d;zTK>nrK zOjPS{@&74-fA7j)8uT9~*g23uGnxwIVj9HorzUX#s0pcp2?GH6i}~+kv9fWChtPa_ z@T3m+$0pbjdQw7jcnHn;Pi85hk_u2-1^}c)LNvjdam8K-XJ+KgKQ%!?2n_!#{$H|| zLO=%;hRo6EDmnOBKCL9Cg~ETU##@u^W_5joZ%Et%X_n##%JDOcsO=0VL|Lkk!VdRJ z^|~2pB@PUspT?NOeO?=0Vb+fAGc!j%Ufn-cB`s2A~W{Zj{`wqWq_-w0wr@6VrM zbzni@8c>WS!7c&|ZR$cQ;`niRw{4kG#e z70e!uX8VmP23SuJ*)#(&R=;SxGAvq|&>geL&!5Z7@0Z(No*W561n#u$Uc`f9pD70# z=sKOSK|bF~#khTTn)B28h^a1{;>EaRnHj~>i=Fnr3+Fa4 z`^+O5_itS#7kPd20rq66_wH`%?HNzWk@XFK0n;Z@Cx{kx==2L22zWH$Yg?7 zvDj|u{{+NR3JvUH({;b*$b(U5U z7(lF!1bz2%06+|-v(D?2KgwNw7( zJB#Tz+ZRi&U$i?f34m7>uTzO#+E5cbaiQ&L}UxyOQq~afbNB4EI{E04ZWg53w0A{O%qo=lF8d zf~ktGvIgf-a~zQoWf>loF7pOodrd0a2|BzwwPDV}ShauTK8*fmF6NRbO>Iw9zZU}u zw8Ya}?seBnEGQDmH#XpUUkj}N49tP<2jYwTFp!P+&Fd(%Z#yo80|5@zN(D{_pNow*&4%ql zW~&yp@scb-+Qj-EmErY+Tu=dUmf@*BoXY2&oKT8U?8?s1d}4a`Aq>7SV800m$FE~? zjmz(LY+Xx9sDX$;vU`xgw*jLw7dWOnWWCO8o|;}f>cu0Q&`0I{YudMn;P;L3R-uz# zfns_mZED_IakFBPP2r_S8XM$X)@O-xVKi4`7373Jkd5{2$M#%cRhWer3M(vr{S6>h zj{givZJ3(`yFL@``(afn&~iNx@B1|-qfYiZu?-_&Z8+R~v`d6R-}EX9IVXWO-!hL5 z*k6T#^2zAXdardU3Ao~I)4DGdAv2bx{4nOK`20rJo>rmk3S2ZDu}))8Z1m}CKigf0 z3L`3Y`{huj`xj9@`$xTZzZc3je?n^yG<8sw$`Y%}9mUsjUR%T!?k^(q)6FH6Af^b6 zlPg~IEwg0y;`t9y;#D+uz!oE4VP&Je!<#q*F?m5L5?J3i@!0J6q#eu z!RRU`-)HeqGi_UJZ(n~|PSNsv+Wgl{P-TvaUQ9j?ZCtvb^37U$sFpBrkT{7Jpd?HpIvj2!}RIq zH{9~+gErN2+}J`>Jvng2hwM`=PLNkc7pkjblKW|+Fk9rc)G1R>Ww>RC=r-|!m-u7( zc(a$9NG}w#PjWNMS~)o=i~WA&4L(YIW25@AL9+H9!?3Y}sv#MOdY{bb9j>p`{?O(P zIvb`n?_(gP2w3P#&91JX*md+bBEr%xUHMVqfB;(f?OPtMnAZ#rm5q5mh;a2f_si2_ z3oXWB?{NF(JtkAn6F(O{z@b76OIqMC$&oJ_&S|YbFJ*)3qVX_uNf5b8(!vGX19hsG z(OP>RmZp29KH9Ge2kKjKigUmOe^K_!UXP`von)PR8Qz$%=EmOB9xS(ZxE_tnyzo}7 z=6~$~9k0M~v}`w={AeqF?_)9q{m8K#6M{a&(;u;O41j)I$^T?lx5(zlebpY@NT&#N zR+1bB)-1-xj}R8uwqwf=iP1GbxBjneCC%UrSdSxK1vM^i9;bUkS#iRZw2H>rS<2<$ zNT3|sDH>{tXb=zq7XZi*K?#Zsa1h1{h5!Tq_YbKFm_*=A5-<~j63he;4`77!|LBlo zR^~tR3yxcU=gDFbshyF6>o0bdp$qmHS7D}m3;^QZq9kBBU|9$N-~oU?G5;jyFR7>z hN`IR97YZXIo@y!QgFWddJ3|0`sjFx!m))><{BI=FK%f8s diff --git a/packages/file_selector/file_selector/example/web/index.html b/packages/file_selector/file_selector/example/web/index.html deleted file mode 100644 index 9b7a438f823a..000000000000 --- a/packages/file_selector/file_selector/example/web/index.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - example - - - - - - - - diff --git a/packages/file_selector/file_selector/example/web/manifest.json b/packages/file_selector/file_selector/example/web/manifest.json deleted file mode 100644 index 8c012917dab7..000000000000 --- a/packages/file_selector/file_selector/example/web/manifest.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "example", - "short_name": "example", - "start_url": ".", - "display": "standalone", - "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" - } - ] -} From 288e46ee3a80cd11aeb21b707032d73073c3e103 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Sun, 11 Oct 2020 19:02:39 -0500 Subject: [PATCH 132/151] Fix pubspec --- .../file_selector/file_selector/pubspec.yaml | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index ed3244de9aea..02d5ba6e35b2 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -1,23 +1,12 @@ name: file_selector -description: Flutter plugin for launching a URL on Android and iOS. Supports - web, phone, SMS, and email schemes. -homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher -version: 0.1.0 - -flutter: - plugin: - platforms: - web: - default_package: file_selector_web +description: Flutter plugin for opening and saving files. +homepage: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector +version: 1.0.0 dependencies: flutter: sdk: flutter - file_selector_platform_interface: - path: ../file_selector_platform_interface - file_selector_web: - path: ../file_selector_web - + file_selector_platform_interface: ^1.0.0 dev_dependencies: flutter_test: From d644c4432432405df7e863793576daf57351363a Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Sun, 11 Oct 2020 19:14:00 -0500 Subject: [PATCH 133/151] Fix CHANGELOG --- packages/file_selector/file_selector/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 5703fc8bcbb3..41264aff6ce5 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,3 +1,3 @@ -## 0.1.0 +## 1.0.0 * Initial Open Source release. From 725dbc5683acfc345b16d1effa9775d57d368043 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Tue, 13 Oct 2020 14:56:37 -0500 Subject: [PATCH 134/151] Restore example folder --- .../file_selector/example/.gitignore | 48 +++ .../file_selector/example/.metadata | 10 + .../file_selector/example/README.md | 16 + .../file_selector/example/lib/main.dart | 287 ++++++++++++++++++ .../file_selector/example/pubspec.yaml | 78 +++++ .../example/test/widget_test.dart | 8 + .../file_selector/example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../file_selector/example/web/index.html | 33 ++ .../file_selector/example/web/manifest.json | 23 ++ 11 files changed, 503 insertions(+) create mode 100644 packages/file_selector/file_selector/example/.gitignore create mode 100644 packages/file_selector/file_selector/example/.metadata create mode 100644 packages/file_selector/file_selector/example/README.md create mode 100644 packages/file_selector/file_selector/example/lib/main.dart create mode 100644 packages/file_selector/file_selector/example/pubspec.yaml create mode 100644 packages/file_selector/file_selector/example/test/widget_test.dart create mode 100644 packages/file_selector/file_selector/example/web/favicon.png create mode 100644 packages/file_selector/file_selector/example/web/icons/Icon-192.png create mode 100644 packages/file_selector/file_selector/example/web/icons/Icon-512.png create mode 100644 packages/file_selector/file_selector/example/web/index.html create mode 100644 packages/file_selector/file_selector/example/web/manifest.json diff --git a/packages/file_selector/file_selector/example/.gitignore b/packages/file_selector/file_selector/example/.gitignore new file mode 100644 index 000000000000..7abd0753cfc3 --- /dev/null +++ b/packages/file_selector/file_selector/example/.gitignore @@ -0,0 +1,48 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Currently only web supported +android/ +ios/ + +# Exceptions to above rules. +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/packages/file_selector/file_selector/example/.metadata b/packages/file_selector/file_selector/example/.metadata new file mode 100644 index 000000000000..897381f2373f --- /dev/null +++ b/packages/file_selector/file_selector/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 7736f3bc90270dcb0480db2ccffbf1d13c28db85 + channel: dev + +project_type: app diff --git a/packages/file_selector/file_selector/example/README.md b/packages/file_selector/file_selector/example/README.md new file mode 100644 index 000000000000..a13562602822 --- /dev/null +++ b/packages/file_selector/file_selector/example/README.md @@ -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. diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart new file mode 100644 index 000000000000..0a383ded9728 --- /dev/null +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -0,0 +1,287 @@ +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:file_selector/file_selector.dart'; + +void main() { + runApp(MyApp()); +} + +/// MyApp is the Main Application +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'File Selector Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: MyHomePage(title: 'File Selector Demo Home Page'), + routes: { + '/save': (context) => SaveTest(), + '/open': (context) => OpenTest(), + }, + ); + } +} + +/// Page for showing an example of saving with file_selector +class SaveTest extends StatefulWidget { + /// Constructor for the SaveTest page + SaveTest({Key key}) : super(key: key); + + @override + _SaveTestState createState() => _SaveTestState(); +} + +class _SaveTestState extends State { + final TextEditingController _fileController = TextEditingController(); + final TextEditingController _nameController = TextEditingController(); + + @override + void dispose() { + _fileController.dispose(); + super.dispose(); + } + + void _saveFile() async { + final path = await getSavePath(); + + final data = Uint8List.fromList(_fileController.text.codeUnits); + + final new_file = (_nameController.text == '') + ? XFile.fromData(data, mimeType: 'text/plain') + : XFile.fromData(data, + mimeType: 'text/plain', name: _nameController.text); + + new_file.saveTo(path); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Save Example"), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _nameController, + decoration: InputDecoration( + hintText: '(Optional) Suggest File Name', + ), + ), + ), + Container( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _fileController, + decoration: InputDecoration( + hintText: 'Enter File Contents', + ), + ), + ), + SizedBox(height: 10), + RaisedButton( + child: Text('Press to save a text file'), + onPressed: () => {_saveFile()}, + ), + ], + ), + ), + ); + } +} + +/// Screen that shows an example of openFile(s) +class OpenTest extends StatefulWidget { + /// Default constructor + OpenTest({Key key}) : super(key: key); + + @override + _OpenTestState createState() => _OpenTestState(); +} + +class _OpenTestState extends State { + void _onOpenImageFile() async { + final typeGroup = XTypeGroup(label: 'images', extensions: ['jpg', 'png']); + + final file = await openFile(acceptedTypeGroups: [typeGroup]); + + await showDialog( + context: context, + builder: (context) { + return ImageDisplay(file: file); + }); + } + + void _onOpenTextFile() async { + final typeGroup = XTypeGroup(label: 'images', extensions: ['txt', 'json']); + + final file = await openFile(acceptedTypeGroups: [typeGroup]); + + await showDialog( + context: context, + builder: (context) { + return TextDisplay(file: file); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Open Example"), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + RaisedButton( + child: Text('Press to open an image file(png, jpg)'), + onPressed: () => _onOpenImageFile(), + ), + RaisedButton( + child: Text('Press to open a text file (json, txt)'), + onPressed: () => _onOpenTextFile(), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog +class TextDisplay extends StatefulWidget { + /// File to display + final XFile file; + + /// Default Constructor + TextDisplay({Key key, @required this.file}) : super(key: key); + + @override + _TextDisplayState createState() => _TextDisplayState(); +} + +class _TextDisplayState extends State { + String fileContents; + + @override + void initState() { + super.initState(); + _getFileContents(); + } + + void _getFileContents() async { + String contents = await widget.file.readAsString(); + setState(() => fileContents = contents); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(widget.file.name), + content: Scrollbar( + child: SingleChildScrollView( + child: Text( + fileContents ?? + 'Opening file contents...\nThis may take a while if your file is large.', + ), + ), + ), + actions: [ + FlatButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} + +/// Widget that displays a text file in a dialog +class ImageDisplay extends StatefulWidget { + /// File to display + final XFile file; + + /// Default Constructor + ImageDisplay({Key key, @required this.file}) : super(key: key); + + @override + _ImageDisplayState createState() => _ImageDisplayState(); +} + +class _ImageDisplayState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(widget.file.name), + content: Image.network(widget.file.path), + actions: [ + FlatButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} + +/// Home Page of the application +class MyHomePage extends StatefulWidget { + /// Constructor for MyHomePage + MyHomePage({Key key, this.title}) : super(key: key); + + /// Title of Home Page + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +/// State of Home Page +class _MyHomePageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox(height: 10), + RaisedButton( + child: Text('Press to try saving a file'), + onPressed: () => Navigator.pushNamed(context, '/save'), + ), + RaisedButton( + child: Text('Press to try opening a file'), + onPressed: () => Navigator.pushNamed(context, '/open'), + ), + ], + ), + ), + ); + } +} diff --git a/packages/file_selector/file_selector/example/pubspec.yaml b/packages/file_selector/file_selector/example/pubspec.yaml new file mode 100644 index 000000000000..58f0abbf2658 --- /dev/null +++ b/packages/file_selector/file_selector/example/pubspec.yaml @@ -0,0 +1,78 @@ +name: example +description: A new Flutter project. + +# The following line prevents the package from being accidentally published to +# pub.dev using `pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=2.7.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + + file_selector: + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^0.1.3 + +dev_dependencies: + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, 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 from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/file_selector/file_selector/example/test/widget_test.dart b/packages/file_selector/file_selector/example/test/widget_test.dart new file mode 100644 index 000000000000..570e0e4768dc --- /dev/null +++ b/packages/file_selector/file_selector/example/test/widget_test.dart @@ -0,0 +1,8 @@ +// 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. + +void main() {} diff --git a/packages/file_selector/file_selector/example/web/favicon.png b/packages/file_selector/file_selector/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector/example/web/icons/Icon-192.png b/packages/file_selector/file_selector/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 zcmZ`-2T+sGz6~)*FVZ`aW+(v>MIm&M-g^@e2u-B-DoB?qO+b1Tq<5uCCv>ESfRum& zp%X;f!~1{tzL__3=gjVJ=j=J>+nMj%ncXj1Q(b|Ckbw{Y0FWpt%4y%$uD=Z*c-x~o zE;IoE;xa#7Ll5nj-e4CuXB&G*IM~D21rCP$*xLXAK8rIMCSHuSu%bL&S3)8YI~vyp@KBu9Ph7R_pvKQ@xv>NQ`dZp(u{Z8K3yOB zn7-AR+d2JkW)KiGx0hosml;+eCXp6+w%@STjFY*CJ?udJ64&{BCbuebcuH;}(($@@ znNlgBA@ZXB)mcl9nbX#F!f_5Z=W>0kh|UVWnf!At4V*LQP%*gPdCXd6P@J4Td;!Ur z<2ZLmwr(NG`u#gDEMP19UcSzRTL@HsK+PnIXbVBT@oHm53DZr?~V(0{rsalAfwgo zEh=GviaqkF;}F_5-yA!1u3!gxaR&Mj)hLuj5Q-N-@Lra{%<4ONja8pycD90&>yMB` zchhd>0CsH`^|&TstH-8+R`CfoWqmTTF_0?zDOY`E`b)cVi!$4xA@oO;SyOjJyP^_j zx^@Gdf+w|FW@DMdOi8=4+LJl$#@R&&=UM`)G!y%6ZzQLoSL%*KE8IO0~&5XYR9 z&N)?goEiWA(YoRfT{06&D6Yuu@Qt&XVbuW@COb;>SP9~aRc+z`m`80pB2o%`#{xD@ zI3RAlukL5L>px6b?QW1Ac_0>ew%NM!XB2(H+1Y3AJC?C?O`GGs`331Nd4ZvG~bMo{lh~GeL zSL|tT*fF-HXxXYtfu5z+T5Mx9OdP7J4g%@oeC2FaWO1D{=NvL|DNZ}GO?O3`+H*SI z=grGv=7dL{+oY0eJFGO!Qe(e2F?CHW(i!!XkGo2tUvsQ)I9ev`H&=;`N%Z{L zO?vV%rDv$y(@1Yj@xfr7Kzr<~0{^T8wM80xf7IGQF_S-2c0)0D6b0~yD7BsCy+(zL z#N~%&e4iAwi4F$&dI7x6cE|B{f@lY5epaDh=2-(4N05VO~A zQT3hanGy_&p+7Fb^I#ewGsjyCEUmSCaP6JDB*=_()FgQ(-pZ28-{qx~2foO4%pM9e z*_63RT8XjgiaWY|*xydf;8MKLd{HnfZ2kM%iq}fstImB-K6A79B~YoPVa@tYN@T_$ zea+9)<%?=Fl!kd(Y!G(-o}ko28hg2!MR-o5BEa_72uj7Mrc&{lRh3u2%Y=Xk9^-qa zBPWaD=2qcuJ&@Tf6ue&)4_V*45=zWk@Z}Q?f5)*z)-+E|-yC4fs5CE6L_PH3=zI8p z*Z3!it{1e5_^(sF*v=0{`U9C741&lub89gdhKp|Y8CeC{_{wYK-LSbp{h)b~9^j!s z7e?Y{Z3pZv0J)(VL=g>l;<}xk=T*O5YR|hg0eg4u98f2IrA-MY+StQIuK-(*J6TRR z|IM(%uI~?`wsfyO6Tgmsy1b3a)j6M&-jgUjVg+mP*oTKdHg?5E`!r`7AE_#?Fc)&a z08KCq>Gc=ne{PCbRvs6gVW|tKdcE1#7C4e`M|j$C5EYZ~Y=jUtc zj`+?p4ba3uy7><7wIokM79jPza``{Lx0)zGWg;FW1^NKY+GpEi=rHJ+fVRGfXO zPHV52k?jxei_!YYAw1HIz}y8ZMwdZqU%ESwMn7~t zdI5%B;U7RF=jzRz^NuY9nM)&<%M>x>0(e$GpU9th%rHiZsIT>_qp%V~ILlyt^V`=d z!1+DX@ah?RnB$X!0xpTA0}lN@9V-ePx>wQ?-xrJr^qDlw?#O(RsXeAvM%}rg0NT#t z!CsT;-vB=B87ShG`GwO;OEbeL;a}LIu=&@9cb~Rsx(ZPNQ!NT7H{@j0e(DiLea>QD zPmpe90gEKHEZ8oQ@6%E7k-Ptn#z)b9NbD@_GTxEhbS+}Bb74WUaRy{w;E|MgDAvHw zL)ycgM7mB?XVh^OzbC?LKFMotw3r@i&VdUV%^Efdib)3@soX%vWCbnOyt@Y4swW925@bt45y0HY3YI~BnnzZYrinFy;L?2D3BAL`UQ zEj))+f>H7~g8*VuWQ83EtGcx`hun$QvuurSMg3l4IP8Fe`#C|N6mbYJ=n;+}EQm;< z!!N=5j1aAr_uEnnzrEV%_E|JpTb#1p1*}5!Ce!R@d$EtMR~%9# zd;h8=QGT)KMW2IKu_fA_>p_und#-;Q)p%%l0XZOXQicfX8M~7?8}@U^ihu;mizj)t zgV7wk%n-UOb z#!P5q?Ex+*Kx@*p`o$q8FWL*E^$&1*!gpv?Za$YO~{BHeGY*5%4HXUKa_A~~^d z=E*gf6&+LFF^`j4$T~dR)%{I)T?>@Ma?D!gi9I^HqvjPc3-v~=qpX1Mne@*rzT&Xw zQ9DXsSV@PqpEJO-g4A&L{F&;K6W60D!_vs?Vx!?w27XbEuJJP&);)^+VF1nHqHBWu z^>kI$M9yfOY8~|hZ9WB!q-9u&mKhEcRjlf2nm_@s;0D#c|@ED7NZE% zzR;>P5B{o4fzlfsn3CkBK&`OSb-YNrqx@N#4CK!>bQ(V(D#9|l!e9(%sz~PYk@8zt zPN9oK78&-IL_F zhsk1$6p;GqFbtB^ZHHP+cjMvA0(LqlskbdYE_rda>gvQLTiqOQ1~*7lg%z*&p`Ry& zRcG^DbbPj_jOKHTr8uk^15Boj6>hA2S-QY(W-6!FIq8h$<>MI>PYYRenQDBamO#Fv zAH5&ImqKBDn0v5kb|8i0wFhUBJTpT!rB-`zK)^SNnRmLraZcPYK7b{I@+}wXVdW-{Ps17qdRA3JatEd?rPV z4@}(DAMf5EqXCr4-B+~H1P#;t@O}B)tIJ(W6$LrK&0plTmnPpb1TKn3?f?Kk``?D+ zQ!MFqOX7JbsXfQrz`-M@hq7xlfNz;_B{^wbpG8des56x(Q)H)5eLeDwCrVR}hzr~= zM{yXR6IM?kXxauLza#@#u?Y|o;904HCqF<8yT~~c-xyRc0-vxofnxG^(x%>bj5r}N zyFT+xnn-?B`ohA>{+ZZQem=*Xpqz{=j8i2TAC#x-m;;mo{{sLB_z(UoAqD=A#*juZ zCv=J~i*O8;F}A^Wf#+zx;~3B{57xtoxC&j^ie^?**T`WT2OPRtC`xj~+3Kprn=rVM zVJ|h5ux%S{dO}!mq93}P+h36mZ5aZg1-?vhL$ke1d52qIiXSE(llCr5i=QUS?LIjc zV$4q=-)aaR4wsrQv}^shL5u%6;`uiSEs<1nG^?$kl$^6DL z43CjY`M*p}ew}}3rXc7Xck@k41jx}c;NgEIhKZ*jsBRZUP-x2cm;F1<5$jefl|ppO zmZd%%?gMJ^g9=RZ^#8Mf5aWNVhjAS^|DQO+q$)oeob_&ZLFL(zur$)); zU19yRm)z<4&4-M}7!9+^Wl}Uk?`S$#V2%pQ*SIH5KI-mn%i;Z7-)m$mN9CnI$G7?# zo`zVrUwoSL&_dJ92YhX5TKqaRkfPgC4=Q&=K+;_aDs&OU0&{WFH}kKX6uNQC6%oUH z2DZa1s3%Vtk|bglbxep-w)PbFG!J17`<$g8lVhqD2w;Z0zGsh-r zxZ13G$G<48leNqR!DCVt9)@}(zMI5w6Wo=N zpP1*3DI;~h2WDWgcKn*f!+ORD)f$DZFwgKBafEZmeXQMAsq9sxP9A)7zOYnkHT9JU zRA`umgmP9d6=PHmFIgx=0$(sjb>+0CHG)K@cPG{IxaJ&Ueo8)0RWgV9+gO7+Bl1(F z7!BslJ2MP*PWJ;x)QXbR$6jEr5q3 z(3}F@YO_P1NyTdEXRLU6fp?9V2-S=E+YaeLL{Y)W%6`k7$(EW8EZSA*(+;e5@jgD^I zaJQ2|oCM1n!A&-8`;#RDcZyk*+RPkn_r8?Ak@agHiSp*qFNX)&i21HE?yuZ;-C<3C zwJGd1lx5UzViP7sZJ&|LqH*mryb}y|%AOw+v)yc`qM)03qyyrqhX?ub`Cjwx2PrR! z)_z>5*!*$x1=Qa-0uE7jy0z`>|Ni#X+uV|%_81F7)b+nf%iz=`fF4g5UfHS_?PHbr zB;0$bK@=di?f`dS(j{l3-tSCfp~zUuva+=EWxJcRfp(<$@vd(GigM&~vaYZ0c#BTs z3ijkxMl=vw5AS&DcXQ%eeKt!uKvh2l3W?&3=dBHU=Gz?O!40S&&~ei2vg**c$o;i89~6DVns zG>9a*`k5)NI9|?W!@9>rzJ;9EJ=YlJTx1r1BA?H`LWijk(rTax9(OAu;q4_wTj-yj z1%W4GW&K4T=uEGb+E!>W0SD_C0RR91 literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector/example/web/icons/Icon-512.png b/packages/file_selector/file_selector/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 zcmd5=2T+s!lYZ%-(h(2@5fr2dC?F^$C=i-}R6$UX8af(!je;W5yC_|HmujSgN*6?W z3knF*TL1$|?oD*=zPbBVex*RUIKsL<(&Rj9%^UD2IK3W?2j>D?eWQgvS-HLymHo9%~|N2Q{~j za?*X-{b9JRowv_*Mh|;*-kPFn>PI;r<#kFaxFqbn?aq|PduQg=2Q;~Qc}#z)_T%x9 zE|0!a70`58wjREmAH38H1)#gof)U3g9FZ^ zF7&-0^Hy{4XHWLoC*hOG(dg~2g6&?-wqcpf{ z&3=o8vw7lMi22jCG9RQbv8H}`+}9^zSk`nlR8?Z&G2dlDy$4#+WOlg;VHqzuE=fM@ z?OI6HEJH4&tA?FVG}9>jAnq_^tlw8NbjNhfqk2rQr?h(F&WiKy03Sn=-;ZJRh~JrD zbt)zLbnabttEZ>zUiu`N*u4sfQaLE8-WDn@tHp50uD(^r-}UsUUu)`!Rl1PozAc!a z?uj|2QDQ%oV-jxUJmJycySBINSKdX{kDYRS=+`HgR2GO19fg&lZKyBFbbXhQV~v~L za^U944F1_GtuFXtvDdDNDvp<`fqy);>Vw=ncy!NB85Tw{&sT5&Ox%-p%8fTS;OzlRBwErvO+ROe?{%q-Zge=%Up|D4L#>4K@Ke=x%?*^_^P*KD zgXueMiS63!sEw@fNLB-i^F|@Oib+S4bcy{eu&e}Xvb^(mA!=U=Xr3||IpV~3K zQWzEsUeX_qBe6fky#M zzOJm5b+l;~>=sdp%i}}0h zO?B?i*W;Ndn02Y0GUUPxERG`3Bjtj!NroLoYtyVdLtl?SE*CYpf4|_${ku2s`*_)k zN=a}V8_2R5QANlxsq!1BkT6$4>9=-Ix4As@FSS;1q^#TXPrBsw>hJ}$jZ{kUHoP+H zvoYiR39gX}2OHIBYCa~6ERRPJ#V}RIIZakUmuIoLF*{sO8rAUEB9|+A#C|@kw5>u0 zBd=F!4I)Be8ycH*)X1-VPiZ+Ts8_GB;YW&ZFFUo|Sw|x~ZajLsp+_3gv((Q#N>?Jz zFBf`~p_#^${zhPIIJY~yo!7$-xi2LK%3&RkFg}Ax)3+dFCjGgKv^1;lUzQlPo^E{K zmCnrwJ)NuSaJEmueEPO@(_6h3f5mFffhkU9r8A8(JC5eOkux{gPmx_$Uv&|hyj)gN zd>JP8l2U&81@1Hc>#*su2xd{)T`Yw< zN$dSLUN}dfx)Fu`NcY}TuZ)SdviT{JHaiYgP4~@`x{&h*Hd>c3K_To9BnQi@;tuoL z%PYQo&{|IsM)_>BrF1oB~+`2_uZQ48z9!)mtUR zdfKE+b*w8cPu;F6RYJiYyV;PRBbThqHBEu_(U{(gGtjM}Zi$pL8Whx}<JwE3RM0F8x7%!!s)UJVq|TVd#hf1zVLya$;mYp(^oZQ2>=ZXU1c$}f zm|7kfk>=4KoQoQ!2&SOW5|JP1)%#55C$M(u4%SP~tHa&M+=;YsW=v(Old9L3(j)`u z2?#fK&1vtS?G6aOt@E`gZ9*qCmyvc>Ma@Q8^I4y~f3gs7*d=ATlP>1S zyF=k&6p2;7dn^8?+!wZO5r~B+;@KXFEn^&C=6ma1J7Au6y29iMIxd7#iW%=iUzq&C=$aPLa^Q zncia$@TIy6UT@69=nbty5epP>*fVW@5qbUcb2~Gg75dNd{COFLdiz3}kODn^U*=@E z0*$7u7Rl2u)=%fk4m8EK1ctR!6%Ve`e!O20L$0LkM#f+)n9h^dn{n`T*^~d+l*Qlx z$;JC0P9+en2Wlxjwq#z^a6pdnD6fJM!GV7_%8%c)kc5LZs_G^qvw)&J#6WSp< zmsd~1-(GrgjC56Pdf6#!dt^y8Rg}!#UXf)W%~PeU+kU`FeSZHk)%sFv++#Dujk-~m zFHvVJC}UBn2jN& zs!@nZ?e(iyZPNo`p1i#~wsv9l@#Z|ag3JR>0#u1iW9M1RK1iF6-RbJ4KYg?B`dET9 zyR~DjZ>%_vWYm*Z9_+^~hJ_|SNTzBKx=U0l9 z9x(J96b{`R)UVQ$I`wTJ@$_}`)_DyUNOso6=WOmQKI1e`oyYy1C&%AQU<0-`(ow)1 zT}gYdwWdm4wW6|K)LcfMe&psE0XGhMy&xS`@vLi|1#Za{D6l@#D!?nW87wcscUZgELT{Cz**^;Zb~7 z(~WFRO`~!WvyZAW-8v!6n&j*PLm9NlN}BuUN}@E^TX*4Or#dMMF?V9KBeLSiLO4?B zcE3WNIa-H{ThrlCoN=XjOGk1dT=xwwrmt<1a)mrRzg{35`@C!T?&_;Q4Ce=5=>z^*zE_c(0*vWo2_#TD<2)pLXV$FlwP}Ik74IdDQU@yhkCr5h zn5aa>B7PWy5NQ!vf7@p_qtC*{dZ8zLS;JetPkHi>IvPjtJ#ThGQD|Lq#@vE2xdl%`x4A8xOln}BiQ92Po zW;0%A?I5CQ_O`@Ad=`2BLPPbBuPUp@Hb%a_OOI}y{Rwa<#h z5^6M}s7VzE)2&I*33pA>e71d78QpF>sNK;?lj^Kl#wU7G++`N_oL4QPd-iPqBhhs| z(uVM}$ItF-onXuuXO}o$t)emBO3Hjfyil@*+GF;9j?`&67GBM;TGkLHi>@)rkS4Nj zAEk;u)`jc4C$qN6WV2dVd#q}2X6nKt&X*}I@jP%Srs%%DS92lpDY^K*Sx4`l;aql$ zt*-V{U&$DM>pdO?%jt$t=vg5|p+Rw?SPaLW zB6nvZ69$ne4Z(s$3=Rf&RX8L9PWMV*S0@R zuIk&ba#s6sxVZ51^4Kon46X^9`?DC9mEhWB3f+o4#2EXFqy0(UTc>GU| zGCJmI|Dn-dX#7|_6(fT)>&YQ0H&&JX3cTvAq(a@ydM4>5Njnuere{J8p;3?1az60* z$1E7Yyxt^ytULeokgDnRVKQw9vzHg1>X@@jM$n$HBlveIrKP5-GJq%iWH#odVwV6cF^kKX(@#%%uQVb>#T6L^mC@)%SMd4DF? zVky!~ge27>cpUP1Vi}Z32lbLV+CQy+T5Wdmva6Fg^lKb!zrg|HPU=5Qu}k;4GVH+x z%;&pN1LOce0w@9i1Mo-Y|7|z}fbch@BPp2{&R-5{GLoeu8@limQmFF zaJRR|^;kW_nw~0V^ zfTnR!Ni*;-%oSHG1yItARs~uxra|O?YJxBzLjpeE-=~TO3Dn`JL5Gz;F~O1u3|FE- zvK2Vve`ylc`a}G`gpHg58Cqc9fMoy1L}7x7T>%~b&irrNMo?np3`q;d3d;zTK>nrK zOjPS{@&74-fA7j)8uT9~*g23uGnxwIVj9HorzUX#s0pcp2?GH6i}~+kv9fWChtPa_ z@T3m+$0pbjdQw7jcnHn;Pi85hk_u2-1^}c)LNvjdam8K-XJ+KgKQ%!?2n_!#{$H|| zLO=%;hRo6EDmnOBKCL9Cg~ETU##@u^W_5joZ%Et%X_n##%JDOcsO=0VL|Lkk!VdRJ z^|~2pB@PUspT?NOeO?=0Vb+fAGc!j%Ufn-cB`s2A~W{Zj{`wqWq_-w0wr@6VrM zbzni@8c>WS!7c&|ZR$cQ;`niRw{4kG#e z70e!uX8VmP23SuJ*)#(&R=;SxGAvq|&>geL&!5Z7@0Z(No*W561n#u$Uc`f9pD70# z=sKOSK|bF~#khTTn)B28h^a1{;>EaRnHj~>i=Fnr3+Fa4 z`^+O5_itS#7kPd20rq66_wH`%?HNzWk@XFK0n;Z@Cx{kx==2L22zWH$Yg?7 zvDj|u{{+NR3JvUH({;b*$b(U5U z7(lF!1bz2%06+|-v(D?2KgwNw7( zJB#Tz+ZRi&U$i?f34m7>uTzO#+E5cbaiQ&L}UxyOQq~afbNB4EI{E04ZWg53w0A{O%qo=lF8d zf~ktGvIgf-a~zQoWf>loF7pOodrd0a2|BzwwPDV}ShauTK8*fmF6NRbO>Iw9zZU}u zw8Ya}?seBnEGQDmH#XpUUkj}N49tP<2jYwTFp!P+&Fd(%Z#yo80|5@zN(D{_pNow*&4%ql zW~&yp@scb-+Qj-EmErY+Tu=dUmf@*BoXY2&oKT8U?8?s1d}4a`Aq>7SV800m$FE~? zjmz(LY+Xx9sDX$;vU`xgw*jLw7dWOnWWCO8o|;}f>cu0Q&`0I{YudMn;P;L3R-uz# zfns_mZED_IakFBPP2r_S8XM$X)@O-xVKi4`7373Jkd5{2$M#%cRhWer3M(vr{S6>h zj{givZJ3(`yFL@``(afn&~iNx@B1|-qfYiZu?-_&Z8+R~v`d6R-}EX9IVXWO-!hL5 z*k6T#^2zAXdardU3Ao~I)4DGdAv2bx{4nOK`20rJo>rmk3S2ZDu}))8Z1m}CKigf0 z3L`3Y`{huj`xj9@`$xTZzZc3je?n^yG<8sw$`Y%}9mUsjUR%T!?k^(q)6FH6Af^b6 zlPg~IEwg0y;`t9y;#D+uz!oE4VP&Je!<#q*F?m5L5?J3i@!0J6q#eu z!RRU`-)HeqGi_UJZ(n~|PSNsv+Wgl{P-TvaUQ9j?ZCtvb^37U$sFpBrkT{7Jpd?HpIvj2!}RIq zH{9~+gErN2+}J`>Jvng2hwM`=PLNkc7pkjblKW|+Fk9rc)G1R>Ww>RC=r-|!m-u7( zc(a$9NG}w#PjWNMS~)o=i~WA&4L(YIW25@AL9+H9!?3Y}sv#MOdY{bb9j>p`{?O(P zIvb`n?_(gP2w3P#&91JX*md+bBEr%xUHMVqfB;(f?OPtMnAZ#rm5q5mh;a2f_si2_ z3oXWB?{NF(JtkAn6F(O{z@b76OIqMC$&oJ_&S|YbFJ*)3qVX_uNf5b8(!vGX19hsG z(OP>RmZp29KH9Ge2kKjKigUmOe^K_!UXP`von)PR8Qz$%=EmOB9xS(ZxE_tnyzo}7 z=6~$~9k0M~v}`w={AeqF?_)9q{m8K#6M{a&(;u;O41j)I$^T?lx5(zlebpY@NT&#N zR+1bB)-1-xj}R8uwqwf=iP1GbxBjneCC%UrSdSxK1vM^i9;bUkS#iRZw2H>rS<2<$ zNT3|sDH>{tXb=zq7XZi*K?#Zsa1h1{h5!Tq_YbKFm_*=A5-<~j63he;4`77!|LBlo zR^~tR3yxcU=gDFbshyF6>o0bdp$qmHS7D}m3;^QZq9kBBU|9$N-~oU?G5;jyFR7>z hN`IR97YZXIo@y!QgFWddJ3|0`sjFx!m))><{BI=FK%f8s literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector/example/web/index.html b/packages/file_selector/file_selector/example/web/index.html new file mode 100644 index 000000000000..9b7a438f823a --- /dev/null +++ b/packages/file_selector/file_selector/example/web/index.html @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + example + + + + + + + + diff --git a/packages/file_selector/file_selector/example/web/manifest.json b/packages/file_selector/file_selector/example/web/manifest.json new file mode 100644 index 000000000000..8c012917dab7 --- /dev/null +++ b/packages/file_selector/file_selector/example/web/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "example", + "short_name": "example", + "start_url": ".", + "display": "standalone", + "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" + } + ] +} From 42852c2fb2a3f0e1f8ca733d855b19240a17d9d4 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Tue, 13 Oct 2020 15:12:26 -0500 Subject: [PATCH 135/151] Change example styles --- .../file_selector/example/lib/main.dart | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 0a383ded9728..23dfeefe3bd3 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -92,6 +92,8 @@ class _SaveTestState extends State { ), SizedBox(height: 10), RaisedButton( + color: Colors.blue, + textColor: Colors.white, child: Text('Press to save a text file'), onPressed: () => {_saveFile()}, ), @@ -147,10 +149,15 @@ class _OpenTestState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ RaisedButton( + color: Colors.blue, + textColor: Colors.white, child: Text('Press to open an image file(png, jpg)'), onPressed: () => _onOpenImageFile(), ), + SizedBox(height: 10), RaisedButton( + color: Colors.blue, + textColor: Colors.white, child: Text('Press to open a text file (json, txt)'), onPressed: () => _onOpenTextFile(), ), @@ -270,12 +277,16 @@ class _MyHomePageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - SizedBox(height: 10), RaisedButton( + color: Colors.blue, + textColor: Colors.white, child: Text('Press to try saving a file'), onPressed: () => Navigator.pushNamed(context, '/save'), ), + SizedBox(height: 10), RaisedButton( + color: Colors.blue, + textColor: Colors.white, child: Text('Press to try opening a file'), onPressed: () => Navigator.pushNamed(context, '/open'), ), From ece9ef299c3ef042874d6f2e077f787a2234f890 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Tue, 13 Oct 2020 17:46:49 -0500 Subject: [PATCH 136/151] Removes example's tests folder --- .../file_selector/example/test/widget_test.dart | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 packages/file_selector/file_selector/example/test/widget_test.dart diff --git a/packages/file_selector/file_selector/example/test/widget_test.dart b/packages/file_selector/file_selector/example/test/widget_test.dart deleted file mode 100644 index 570e0e4768dc..000000000000 --- a/packages/file_selector/file_selector/example/test/widget_test.dart +++ /dev/null @@ -1,8 +0,0 @@ -// 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. - -void main() {} From 67afdb509d2326937a77d424722fd15bdb3dc983 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Fri, 16 Oct 2020 15:30:30 -0500 Subject: [PATCH 137/151] Change version --- packages/file_selector/file_selector/CHANGELOG.md | 2 +- packages/file_selector/file_selector/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 41264aff6ce5..903b60c42466 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,3 +1,3 @@ -## 1.0.0 +## 0.0.1 * Initial Open Source release. diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 02d5ba6e35b2..8ca9957dcc9e 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -1,7 +1,7 @@ name: file_selector description: Flutter plugin for opening and saving files. homepage: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector -version: 1.0.0 +version: 0.0.1 dependencies: flutter: From e4ed98d9ffe71c36c1d46af9d0294a28b4f4fe5c Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Thu, 22 Oct 2020 17:05:14 -0500 Subject: [PATCH 138/151] Change version to 0.7.0 --- packages/file_selector/file_selector/CHANGELOG.md | 2 +- packages/file_selector/file_selector/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 903b60c42466..8dab88a33cef 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,3 +1,3 @@ -## 0.0.1 +## 0.7.0 * Initial Open Source release. diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 8ca9957dcc9e..5a90f048c314 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -1,7 +1,7 @@ name: file_selector description: Flutter plugin for opening and saving files. homepage: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector -version: 0.0.1 +version: 0.7.0 dependencies: flutter: From 89171160960e87be614e394fa1b407b58d2977ba Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Fri, 23 Oct 2020 12:36:02 -0500 Subject: [PATCH 139/151] Improve the Readme template --- packages/file_selector/file_selector/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index db0f152bc5e2..bbb72504251e 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -1,3 +1,13 @@ # file_selector +[![pub package](https://img.shields.io/pub/v/file_selector.svg)](https://pub.dartlang.org/packages/file_selector) + A Flutter plugin that manages files and interactions with file dialogs. + +## Usage +To use this plugin, add `file_selector` as a [dependency in your pubspec.yaml file](https://flutter.dev/platform-plugins/). + +### Example + +``` dart +``` From 1c2c95666b04ffb44f45a051ea612fc3e49cc1be Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Fri, 23 Oct 2020 12:40:55 -0500 Subject: [PATCH 140/151] Improve the example's Readme template --- .../file_selector/example/README.md | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/file_selector/file_selector/example/README.md b/packages/file_selector/file_selector/example/README.md index a13562602822..93260dc716b2 100644 --- a/packages/file_selector/file_selector/example/README.md +++ b/packages/file_selector/file_selector/example/README.md @@ -1,16 +1,8 @@ -# example +# file_selector_example -A new Flutter project. +Demonstrates how to use the file_selector plugin. ## 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. +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). From 422f1b47c31e5280e1d1ba29e4acf9643b325f72 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Fri, 23 Oct 2020 15:04:55 -0500 Subject: [PATCH 141/151] Improves the README --- .../file_selector/file_selector/README.md | 25 ++++++++++++++++++- .../file_selector/example/lib/main.dart | 4 +-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index bbb72504251e..e0b974513fb2 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -7,7 +7,30 @@ A Flutter plugin that manages files and interactions with file dialogs. ## Usage To use this plugin, add `file_selector` as a [dependency in your pubspec.yaml file](https://flutter.dev/platform-plugins/). -### Example +### Examples +Here are small examples that show you how to use the API. +Please also take a look at our [example][example] app. +#### Open a single file ``` dart +final typeGroup = XTypeGroup(label: 'images', extensions: ['jpg', 'png']); +final file = await openFile(acceptedTypeGroups: [typeGroup]); ``` + +#### Open multiple files at once +``` dart +final typeGroup = XTypeGroup(label: 'images', extensions: ['jpg', 'png']); +final files = await openFiles(acceptedTypeGroups: [typeGroup]); +``` + +#### Saving a file +```dart +final path = await getSavePath(); +final name = "hello_file_selector.txt"; +final data = Uint8List.fromList("Hello World!".codeUnits); +final mimeType = "text/plain"; +final file = XFile.fromData(data, name: name, mimeType: mimeType); +await file.saveTo(path); +``` + +[example]:./example diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 23dfeefe3bd3..60f773d4f6c7 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -48,14 +48,14 @@ class _SaveTestState extends State { void _saveFile() async { final path = await getSavePath(); - final data = Uint8List.fromList(_fileController.text.codeUnits); + final data = Uint8List.fromList("Hola que haces".codeUnits); final new_file = (_nameController.text == '') ? XFile.fromData(data, mimeType: 'text/plain') : XFile.fromData(data, mimeType: 'text/plain', name: _nameController.text); - new_file.saveTo(path); + await new_file.saveTo(path); } @override From 4b09ffc6cbe0eb244ad16cd607dbf54f63c807fa Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Fri, 23 Oct 2020 15:52:44 -0500 Subject: [PATCH 142/151] Improve the README --- packages/file_selector/file_selector/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index e0b974513fb2..22ae7073ca2d 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -8,7 +8,7 @@ A Flutter plugin that manages files and interactions with file dialogs. To use this plugin, add `file_selector` as a [dependency in your pubspec.yaml file](https://flutter.dev/platform-plugins/). ### Examples -Here are small examples that show you how to use the API. +Here are small examples that show you how to use the API. Please also take a look at our [example][example] app. #### Open a single file From 9dc80ba0d99a825d48785a38ad60933608942483 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Fri, 30 Oct 2020 13:06:07 -0600 Subject: [PATCH 143/151] Revamp example --- .../file_selector/example/lib/home_page.dart | 40 +++ .../file_selector/example/lib/main.dart | 285 +----------------- .../example/lib/open_image_page.dart | 74 +++++ .../example/lib/open_text_page.dart | 72 +++++ .../example/lib/save_text_page.dart | 65 ++++ 5 files changed, 259 insertions(+), 277 deletions(-) create mode 100644 packages/file_selector/file_selector/example/lib/home_page.dart create mode 100644 packages/file_selector/file_selector/example/lib/open_image_page.dart create mode 100644 packages/file_selector/file_selector/example/lib/open_text_page.dart create mode 100644 packages/file_selector/file_selector/example/lib/save_text_page.dart diff --git a/packages/file_selector/file_selector/example/lib/home_page.dart b/packages/file_selector/file_selector/example/lib/home_page.dart new file mode 100644 index 000000000000..2d499e39eb48 --- /dev/null +++ b/packages/file_selector/file_selector/example/lib/home_page.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; + +/// Home Page of the application +class HomePage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('File Selector Demo Home Page'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + RaisedButton( + color: Colors.blue, + textColor: Colors.white, + child: Text('Open a text file'), + onPressed: () => Navigator.pushNamed(context, '/open/text'), + ), + SizedBox(height: 10), + RaisedButton( + color: Colors.blue, + textColor: Colors.white, + child: Text('Open an image'), + onPressed: () => Navigator.pushNamed(context, '/open/image'), + ), + SizedBox(height: 10), + RaisedButton( + color: Colors.blue, + textColor: Colors.white, + child: Text('Save text into a file'), + onPressed: () => Navigator.pushNamed(context, '/save/text'), + ), + ], + ), + ), + ); + } +} diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 60f773d4f6c7..0db1f925b07d 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -1,7 +1,8 @@ -import 'dart:typed_data'; - import 'package:flutter/material.dart'; -import 'package:file_selector/file_selector.dart'; +import 'package:example/home_page.dart'; +import 'package:example/save_text_page.dart'; +import 'package:example/open_text_page.dart'; +import 'package:example/open_image_page.dart'; void main() { runApp(MyApp()); @@ -17,282 +18,12 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: MyHomePage(title: 'File Selector Demo Home Page'), + home: HomePage(), routes: { - '/save': (context) => SaveTest(), - '/open': (context) => OpenTest(), + '/save/text': (context) => SaveTextPage(), + '/open/text': (context) => OpenTextPage(), + '/open/image': (context) => OpenImagePage(), }, ); } } - -/// Page for showing an example of saving with file_selector -class SaveTest extends StatefulWidget { - /// Constructor for the SaveTest page - SaveTest({Key key}) : super(key: key); - - @override - _SaveTestState createState() => _SaveTestState(); -} - -class _SaveTestState extends State { - final TextEditingController _fileController = TextEditingController(); - final TextEditingController _nameController = TextEditingController(); - - @override - void dispose() { - _fileController.dispose(); - super.dispose(); - } - - void _saveFile() async { - final path = await getSavePath(); - - final data = Uint8List.fromList("Hola que haces".codeUnits); - - final new_file = (_nameController.text == '') - ? XFile.fromData(data, mimeType: 'text/plain') - : XFile.fromData(data, - mimeType: 'text/plain', name: _nameController.text); - - await new_file.saveTo(path); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text("Save Example"), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: 300, - child: TextField( - minLines: 1, - maxLines: 12, - controller: _nameController, - decoration: InputDecoration( - hintText: '(Optional) Suggest File Name', - ), - ), - ), - Container( - width: 300, - child: TextField( - minLines: 1, - maxLines: 12, - controller: _fileController, - decoration: InputDecoration( - hintText: 'Enter File Contents', - ), - ), - ), - SizedBox(height: 10), - RaisedButton( - color: Colors.blue, - textColor: Colors.white, - child: Text('Press to save a text file'), - onPressed: () => {_saveFile()}, - ), - ], - ), - ), - ); - } -} - -/// Screen that shows an example of openFile(s) -class OpenTest extends StatefulWidget { - /// Default constructor - OpenTest({Key key}) : super(key: key); - - @override - _OpenTestState createState() => _OpenTestState(); -} - -class _OpenTestState extends State { - void _onOpenImageFile() async { - final typeGroup = XTypeGroup(label: 'images', extensions: ['jpg', 'png']); - - final file = await openFile(acceptedTypeGroups: [typeGroup]); - - await showDialog( - context: context, - builder: (context) { - return ImageDisplay(file: file); - }); - } - - void _onOpenTextFile() async { - final typeGroup = XTypeGroup(label: 'images', extensions: ['txt', 'json']); - - final file = await openFile(acceptedTypeGroups: [typeGroup]); - - await showDialog( - context: context, - builder: (context) { - return TextDisplay(file: file); - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text("Open Example"), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - RaisedButton( - color: Colors.blue, - textColor: Colors.white, - child: Text('Press to open an image file(png, jpg)'), - onPressed: () => _onOpenImageFile(), - ), - SizedBox(height: 10), - RaisedButton( - color: Colors.blue, - textColor: Colors.white, - child: Text('Press to open a text file (json, txt)'), - onPressed: () => _onOpenTextFile(), - ), - ], - ), - ), - ); - } -} - -/// Widget that displays a text file in a dialog -class TextDisplay extends StatefulWidget { - /// File to display - final XFile file; - - /// Default Constructor - TextDisplay({Key key, @required this.file}) : super(key: key); - - @override - _TextDisplayState createState() => _TextDisplayState(); -} - -class _TextDisplayState extends State { - String fileContents; - - @override - void initState() { - super.initState(); - _getFileContents(); - } - - void _getFileContents() async { - String contents = await widget.file.readAsString(); - setState(() => fileContents = contents); - } - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: Text(widget.file.name), - content: Scrollbar( - child: SingleChildScrollView( - child: Text( - fileContents ?? - 'Opening file contents...\nThis may take a while if your file is large.', - ), - ), - ), - actions: [ - FlatButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ); - } -} - -/// Widget that displays a text file in a dialog -class ImageDisplay extends StatefulWidget { - /// File to display - final XFile file; - - /// Default Constructor - ImageDisplay({Key key, @required this.file}) : super(key: key); - - @override - _ImageDisplayState createState() => _ImageDisplayState(); -} - -class _ImageDisplayState extends State { - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: Text(widget.file.name), - content: Image.network(widget.file.path), - actions: [ - FlatButton( - child: const Text('Close'), - onPressed: () { - Navigator.pop(context); - }, - ), - ], - ); - } -} - -/// Home Page of the application -class MyHomePage extends StatefulWidget { - /// Constructor for MyHomePage - MyHomePage({Key key, this.title}) : super(key: key); - - /// Title of Home Page - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -/// State of Home Page -class _MyHomePageState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - RaisedButton( - color: Colors.blue, - textColor: Colors.white, - child: Text('Press to try saving a file'), - onPressed: () => Navigator.pushNamed(context, '/save'), - ), - SizedBox(height: 10), - RaisedButton( - color: Colors.blue, - textColor: Colors.white, - child: Text('Press to try opening a file'), - onPressed: () => Navigator.pushNamed(context, '/open'), - ), - ], - ), - ), - ); - } -} diff --git a/packages/file_selector/file_selector/example/lib/open_image_page.dart b/packages/file_selector/file_selector/example/lib/open_image_page.dart new file mode 100644 index 000000000000..646250803d67 --- /dev/null +++ b/packages/file_selector/file_selector/example/lib/open_image_page.dart @@ -0,0 +1,74 @@ +import 'dart:io'; +import 'package:flutter/foundation.dart'; +import 'package:file_selector/file_selector.dart'; +import 'package:flutter/material.dart'; + +/// Screen that shows an example of openFile(s) +class OpenImagePage extends StatelessWidget { + void _openImageFile(BuildContext context) async { + final XTypeGroup typeGroup = XTypeGroup( + label: 'images', + extensions: ['jpg', 'png'], + ); + final XFile file = await openFile(acceptedTypeGroups: [typeGroup]); + final String fileName = file.name; + final String filePath = file.path; + + await showDialog( + context: context, + builder: (context) => ImageDisplay(fileName, filePath), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Open an image"), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + RaisedButton( + color: Colors.blue, + textColor: Colors.white, + child: Text('Press to open an image file(png, jpg)'), + onPressed: () => _openImageFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog +class ImageDisplay extends StatelessWidget { + /// Image's name + final String fileName; + + /// Image's path + final String filePath; + + /// Default Constructor + ImageDisplay(this.fileName, this.filePath); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(fileName), + // On web the filePath is a blob url + // while on other platforms it is a system path. + content: kIsWeb ? Image.network(filePath) : Image.file(File(filePath)), + actions: [ + FlatButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector/example/lib/open_text_page.dart b/packages/file_selector/file_selector/example/lib/open_text_page.dart new file mode 100644 index 000000000000..b1b7947a7af0 --- /dev/null +++ b/packages/file_selector/file_selector/example/lib/open_text_page.dart @@ -0,0 +1,72 @@ +import 'package:file_selector/file_selector.dart'; +import 'package:flutter/material.dart'; + +/// Screen that shows an example of openFile(s) +class OpenTextPage extends StatelessWidget { + void _openTextFile(BuildContext context) async { + final XTypeGroup typeGroup = XTypeGroup( + label: 'text', + extensions: ['txt', 'json'], + ); + final XFile file = await openFile(acceptedTypeGroups: [typeGroup]); + final String fileName = file.name; + final String fileContent = await file.readAsString(); + + await showDialog( + context: context, + builder: (context) => TextDisplay(fileName, fileContent), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Open a text file"), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + RaisedButton( + color: Colors.blue, + textColor: Colors.white, + child: Text('Press to open a text file (json, txt)'), + onPressed: () => _openTextFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog +class TextDisplay extends StatelessWidget { + /// File's name + final String fileName; + + /// File to display + final String fileContent; + + /// Default Constructor + TextDisplay(this.fileName, this.fileContent); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(fileName), + content: Scrollbar( + child: SingleChildScrollView( + child: Text(fileContent), + ), + ), + actions: [ + FlatButton( + child: const Text('Close'), + onPressed: () => Navigator.pop(context), + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector/example/lib/save_text_page.dart b/packages/file_selector/file_selector/example/lib/save_text_page.dart new file mode 100644 index 000000000000..b70231f5a410 --- /dev/null +++ b/packages/file_selector/file_selector/example/lib/save_text_page.dart @@ -0,0 +1,65 @@ +import 'dart:typed_data'; +import 'package:file_selector/file_selector.dart'; +import 'package:flutter/material.dart'; + +/// Page for showing an example of saving with file_selector +class SaveTextPage extends StatelessWidget { + final TextEditingController _nameController = TextEditingController(); + final TextEditingController _contentController = TextEditingController(); + + void _saveFile() async { + final String path = await getSavePath(); + final String text = _contentController.text; + final String fileName = _nameController.text; + final Uint8List fileData = Uint8List.fromList(text.codeUnits); + final String fileMimeType = 'text/plain'; + final XFile textFile = + XFile.fromData(fileData, mimeType: fileMimeType, name: fileName); + await textFile.saveTo(path); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Save text into a file"), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _nameController, + decoration: InputDecoration( + hintText: '(Optional) Suggest File Name', + ), + ), + ), + Container( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _contentController, + decoration: InputDecoration( + hintText: 'Enter File Contents', + ), + ), + ), + SizedBox(height: 10), + RaisedButton( + color: Colors.blue, + textColor: Colors.white, + child: Text('Press to save a text file'), + onPressed: () => _saveFile(), + ), + ], + ), + ), + ); + } +} From 8e98c95400904722af4d1e012907d35d7e7b3aae Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Fri, 30 Oct 2020 13:31:19 -0600 Subject: [PATCH 144/151] Improve tests --- .../file_selector/test/file_selector_test.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index 44215ecbf020..dc6885fccae4 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -14,7 +14,6 @@ void main() { test('getSavePath', () async { final expectedPath = '/example/path'; - final typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); when(mock.getSavePath( @@ -49,7 +48,6 @@ void main() { test('openFile', () async { final file = XFile('path'); - final typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); when(mock.openFile( @@ -63,12 +61,11 @@ void main() { initialDirectory: 'dir', confirmButtonText: 'load'); - expect(result, isNotNull); + expect(result, file); }); test('openFiles', () async { final file = XFile('path'); - final typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); when(mock.openFiles( @@ -82,7 +79,7 @@ void main() { initialDirectory: 'dir', confirmButtonText: 'load'); - expect(result, isNotNull); + expect(result, [file]); }); } From 4d8bd6840c19ca7a9963bf5d37556ffd65d5837d Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Fri, 30 Oct 2020 15:25:25 -0600 Subject: [PATCH 145/151] Add open multiple images example --- .../file_selector/example/lib/home_page.dart | 7 ++ .../file_selector/example/lib/main.dart | 2 + .../example/lib/open_image_page.dart | 3 +- .../lib/open_multiple_images_page.dart | 84 +++++++++++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart diff --git a/packages/file_selector/file_selector/example/lib/home_page.dart b/packages/file_selector/file_selector/example/lib/home_page.dart index 2d499e39eb48..2c0128fef2ed 100644 --- a/packages/file_selector/file_selector/example/lib/home_page.dart +++ b/packages/file_selector/file_selector/example/lib/home_page.dart @@ -26,6 +26,13 @@ class HomePage extends StatelessWidget { onPressed: () => Navigator.pushNamed(context, '/open/image'), ), SizedBox(height: 10), + RaisedButton( + color: Colors.blue, + textColor: Colors.white, + child: Text('Open multiple images'), + onPressed: () => Navigator.pushNamed(context, '/open/images'), + ), + SizedBox(height: 10), RaisedButton( color: Colors.blue, textColor: Colors.white, diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 0db1f925b07d..d17b6eee379f 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -3,6 +3,7 @@ import 'package:example/home_page.dart'; import 'package:example/save_text_page.dart'; import 'package:example/open_text_page.dart'; import 'package:example/open_image_page.dart'; +import 'package:example/open_multiple_images_page.dart'; void main() { runApp(MyApp()); @@ -23,6 +24,7 @@ class MyApp extends StatelessWidget { '/save/text': (context) => SaveTextPage(), '/open/text': (context) => OpenTextPage(), '/open/image': (context) => OpenImagePage(), + '/open/images': (context) => OpenMultipleImagesPage(), }, ); } diff --git a/packages/file_selector/file_selector/example/lib/open_image_page.dart b/packages/file_selector/file_selector/example/lib/open_image_page.dart index 646250803d67..1428aa2141a7 100644 --- a/packages/file_selector/file_selector/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_image_page.dart @@ -10,7 +10,8 @@ class OpenImagePage extends StatelessWidget { label: 'images', extensions: ['jpg', 'png'], ); - final XFile file = await openFile(acceptedTypeGroups: [typeGroup]); + final List files = await openFiles(acceptedTypeGroups: [typeGroup]); + final XFile file = files[0]; final String fileName = file.name; final String filePath = file.path; diff --git a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart new file mode 100644 index 000000000000..49d47e82956c --- /dev/null +++ b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart @@ -0,0 +1,84 @@ +import 'dart:io'; +import 'package:flutter/foundation.dart'; +import 'package:file_selector/file_selector.dart'; +import 'package:flutter/material.dart'; + +/// Screen that shows an example of openFile(s) +class OpenMultipleImagesPage extends StatelessWidget { + void _openImageFile(BuildContext context) async { + final XTypeGroup jpgsTypeGroup = XTypeGroup( + label: 'images', + extensions: ['jpg', 'jpeg'], + ); + final XTypeGroup pngTypeGroup = XTypeGroup( + extensions: ['png'], + ); + final List files = await openFiles(acceptedTypeGroups: [ + jpgsTypeGroup, + pngTypeGroup, + ]); + await showDialog( + context: context, + builder: (context) => MultipleImagesDisplay(files), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Open multiple images"), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + RaisedButton( + color: Colors.blue, + textColor: Colors.white, + child: Text('Press to open multiple images (png, jpg)'), + onPressed: () => _openImageFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog +class MultipleImagesDisplay extends StatelessWidget { + final List files; + + /// Default Constructor + MultipleImagesDisplay(this.files); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text('Gallery'), + // On web the filePath is a blob url + // while on other platforms it is a system path. + content: Center( + child: Row( + children: [ + ...files.map( + (file) => Flexible( + child: kIsWeb + ? Image.network(file.path) + : Image.file(File(file.path))), + ) + ], + ), + ), + actions: [ + FlatButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} From 2284daad931b4893eaf73ff800831b62866e0fe3 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Fri, 30 Oct 2020 15:45:45 -0600 Subject: [PATCH 146/151] Add documentation to public field --- .../file_selector/example/lib/open_multiple_images_page.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart index 49d47e82956c..a037252def1f 100644 --- a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart @@ -48,6 +48,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog class MultipleImagesDisplay extends StatelessWidget { + /// The files containing the images final List files; /// Default Constructor From 208e5042d2559624a82c427acf11903540e8acd5 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Fri, 6 Nov 2020 15:03:30 -0600 Subject: [PATCH 147/151] Add get directory example --- .../example/lib/get_directory_page.dart | 66 +++++++++++++++++++ .../file_selector/example/lib/home_page.dart | 9 ++- .../file_selector/example/lib/main.dart | 8 ++- 3 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 packages/file_selector/file_selector/example/lib/get_directory_page.dart diff --git a/packages/file_selector/file_selector/example/lib/get_directory_page.dart b/packages/file_selector/file_selector/example/lib/get_directory_page.dart new file mode 100644 index 000000000000..5e4378a8fcb1 --- /dev/null +++ b/packages/file_selector/file_selector/example/lib/get_directory_page.dart @@ -0,0 +1,66 @@ +import 'package:file_selector/file_selector.dart'; +import 'package:flutter/material.dart'; + +/// Screen that shows an example of openFile(s) +class GetDirectoryPage extends StatelessWidget { + void _getDirectoryPath(BuildContext context) async { + final String initialDirectory = '~/Desktop'; + final String confirmButtonText = 'Choose a cool directory'; + final String directoryPath = await getDirectoryPath( + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText); + await showDialog( + context: context, + builder: (context) => TextDisplay(directoryPath), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Open a text file"), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + RaisedButton( + color: Colors.blue, + textColor: Colors.white, + child: Text('Press to ask user to choose a directory'), + onPressed: () => _getDirectoryPath(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog +class TextDisplay extends StatelessWidget { + /// Directory path + final String directoryPath; + + /// Default Constructor + TextDisplay(this.directoryPath); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text('Selected Directory'), + content: Scrollbar( + child: SingleChildScrollView( + child: Text(directoryPath), + ), + ), + actions: [ + FlatButton( + child: const Text('Close'), + onPressed: () => Navigator.pop(context), + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector/example/lib/home_page.dart b/packages/file_selector/file_selector/example/lib/home_page.dart index 2c0128fef2ed..c37d90170f6e 100644 --- a/packages/file_selector/file_selector/example/lib/home_page.dart +++ b/packages/file_selector/file_selector/example/lib/home_page.dart @@ -36,9 +36,16 @@ class HomePage extends StatelessWidget { RaisedButton( color: Colors.blue, textColor: Colors.white, - child: Text('Save text into a file'), + child: Text('Save a file'), onPressed: () => Navigator.pushNamed(context, '/save/text'), ), + SizedBox(height: 10), + RaisedButton( + color: Colors.blue, + textColor: Colors.white, + child: Text('Open a get directory dialog'), + onPressed: () => Navigator.pushNamed(context, '/directory'), + ), ], ), ), diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index d17b6eee379f..bb34918e36a3 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -1,9 +1,10 @@ import 'package:flutter/material.dart'; import 'package:example/home_page.dart'; -import 'package:example/save_text_page.dart'; +import 'package:example/get_directory_page.dart'; import 'package:example/open_text_page.dart'; import 'package:example/open_image_page.dart'; import 'package:example/open_multiple_images_page.dart'; +import 'package:example/save_text_page.dart'; void main() { runApp(MyApp()); @@ -21,10 +22,11 @@ class MyApp extends StatelessWidget { ), home: HomePage(), routes: { - '/save/text': (context) => SaveTextPage(), - '/open/text': (context) => OpenTextPage(), '/open/image': (context) => OpenImagePage(), '/open/images': (context) => OpenMultipleImagesPage(), + '/open/text': (context) => OpenTextPage(), + '/save/text': (context) => SaveTextPage(), + '/directory': (context) => GetDirectoryPage(), }, ); } From 3d2cf35d50d4f2b57d046d0500049f6520fcac9c Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Mon, 16 Nov 2020 17:14:59 -0600 Subject: [PATCH 148/151] Improve tests --- .../test/file_selector_test.dart | 270 ++++++++++++++---- 1 file changed, 214 insertions(+), 56 deletions(-) diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index dc6885fccae4..15756cc2b622 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -9,77 +9,235 @@ import 'package:file_selector/file_selector.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; void main() { - final mock = MockFileSelector(); - FileSelectorPlatform.instance = mock; - - test('getSavePath', () async { - final expectedPath = '/example/path'; - final typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); - - when(mock.getSavePath( - acceptedTypeGroups: [typeGroup], - initialDirectory: 'dir', - suggestedName: 'name', - confirmButtonText: 'save', - )).thenAnswer((_) => Future.value(expectedPath)); - - final result = await getSavePath( - acceptedTypeGroups: [typeGroup], - initialDirectory: 'dir', - suggestedName: 'name', - confirmButtonText: 'save'); - - expect(result, expectedPath); + MockFileSelector mock; + final initialDirectory = '/home/flutteruser'; + final confirmButtonText = 'Use this profile picture'; + final suggestedName = 'suggested_name'; + final acceptedTypeGroups = [ + XTypeGroup(label: 'documents', mimeTypes: [ + 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessing', + ]), + XTypeGroup(label: 'images', extensions: [ + 'jpg', + 'png', + ]), + ]; + + setUp(() { + mock = MockFileSelector(); + FileSelectorPlatform.instance = mock; }); - test('getDirectoryPath', () async { - final expectedPath = '/example/path'; + group('openFile', () { + final expectedFile = XFile('path'); - when(mock.getDirectoryPath( - initialDirectory: 'dir', - confirmButtonText: 'save', - )).thenAnswer((_) => Future.value(expectedPath)); + test('works', () async { + when(mock.openFile( + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText, + acceptedTypeGroups: acceptedTypeGroups, + )).thenAnswer((_) => Future.value(expectedFile)); - final result = await getDirectoryPath( - initialDirectory: 'dir', confirmButtonText: 'save'); + final file = await openFile( + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText, + acceptedTypeGroups: acceptedTypeGroups, + ); - expect(result, expectedPath); + expect(file, expectedFile); + }); + + test('works with no arguments', () async { + when(mock.openFile()).thenAnswer((_) => Future.value(expectedFile)); + + final file = await openFile(); + + expect(file, expectedFile); + }); + + test('sets the initial directory', () async { + when(mock.openFile(initialDirectory: initialDirectory)) + .thenAnswer((_) => Future.value(expectedFile)); + + final file = await openFile(initialDirectory: initialDirectory); + expect(file, expectedFile); + }); + + test('sets the button confirmation label', () async { + when(mock.openFile(confirmButtonText: confirmButtonText)) + .thenAnswer((_) => Future.value(expectedFile)); + + final file = await openFile(confirmButtonText: confirmButtonText); + expect(file, expectedFile); + }); + + test('sets the accepted type groups', () async { + when(mock.openFile(acceptedTypeGroups: acceptedTypeGroups)) + .thenAnswer((_) => Future.value(expectedFile)); + + final file = await openFile(acceptedTypeGroups: acceptedTypeGroups); + expect(file, expectedFile); + }); }); - test('openFile', () async { - final file = XFile('path'); - final typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); + group('openFiles', () { + final expectedFiles = [XFile('path')]; + + test('works', () async { + when(mock.openFiles( + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText, + acceptedTypeGroups: acceptedTypeGroups, + )).thenAnswer((_) => Future.value(expectedFiles)); + + final file = await openFiles( + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText, + acceptedTypeGroups: acceptedTypeGroups, + ); + + expect(file, expectedFiles); + }); + + test('works with no arguments', () async { + when(mock.openFiles()).thenAnswer((_) => Future.value(expectedFiles)); - when(mock.openFile( - acceptedTypeGroups: [typeGroup], - initialDirectory: 'dir', - confirmButtonText: 'load', - )).thenAnswer((_) => Future.value(file)); + final files = await openFiles(); - final result = await openFile( - acceptedTypeGroups: [typeGroup], - initialDirectory: 'dir', - confirmButtonText: 'load'); + expect(files, expectedFiles); + }); - expect(result, file); + test('sets the initial directory', () async { + when(mock.openFiles(initialDirectory: initialDirectory)) + .thenAnswer((_) => Future.value(expectedFiles)); + + final files = await openFiles(initialDirectory: initialDirectory); + expect(files, expectedFiles); + }); + + test('sets the button confirmation label', () async { + when(mock.openFiles(confirmButtonText: confirmButtonText)) + .thenAnswer((_) => Future.value(expectedFiles)); + + final files = await openFiles(confirmButtonText: confirmButtonText); + expect(files, expectedFiles); + }); + + test('sets the accepted type groups', () async { + when(mock.openFiles(acceptedTypeGroups: acceptedTypeGroups)) + .thenAnswer((_) => Future.value(expectedFiles)); + + final files = await openFiles(acceptedTypeGroups: acceptedTypeGroups); + expect(files, expectedFiles); + }); + }); + + group('getSavePath', () { + final expectedSavePath = '/example/path'; + + test('works', () async { + when(mock.getSavePath( + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText, + acceptedTypeGroups: acceptedTypeGroups, + suggestedName: suggestedName, + )).thenAnswer((_) => Future.value(expectedSavePath)); + + final savePath = await getSavePath( + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText, + acceptedTypeGroups: acceptedTypeGroups, + suggestedName: suggestedName, + ); + + expect(savePath, expectedSavePath); + }); + + test('works with no arguments', () async { + when(mock.getSavePath()) + .thenAnswer((_) => Future.value(expectedSavePath)); + + final savePath = await getSavePath(); + expect(savePath, expectedSavePath); + }); + + test('sets the initial directory', () async { + when(mock.getSavePath(initialDirectory: initialDirectory)) + .thenAnswer((_) => Future.value(expectedSavePath)); + + final savePath = await getSavePath(initialDirectory: initialDirectory); + expect(savePath, expectedSavePath); + }); + + test('sets the button confirmation label', () async { + when(mock.getSavePath(confirmButtonText: confirmButtonText)) + .thenAnswer((_) => Future.value(expectedSavePath)); + + final savePath = await getSavePath(confirmButtonText: confirmButtonText); + expect(savePath, expectedSavePath); + }); + + test('sets the accepted type groups', () async { + when(mock.getSavePath(acceptedTypeGroups: acceptedTypeGroups)) + .thenAnswer((_) => Future.value(expectedSavePath)); + + final savePath = + await getSavePath(acceptedTypeGroups: acceptedTypeGroups); + expect(savePath, expectedSavePath); + }); + + test('sets the suggested name', () async { + when(mock.getSavePath(suggestedName: suggestedName)) + .thenAnswer((_) => Future.value(expectedSavePath)); + + final savePath = await getSavePath(suggestedName: suggestedName); + expect(savePath, expectedSavePath); + }); }); - test('openFiles', () async { - final file = XFile('path'); - final typeGroup = XTypeGroup(label: 'group', extensions: ['jpg', 'png']); + group('getDirectoryPath', () { + final expectedDirectoryPath = '/example/path'; + + test('works', () async { + when(mock.getDirectoryPath( + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText, + )).thenAnswer((_) => Future.value(expectedDirectoryPath)); + + final directoryPath = await getDirectoryPath( + initialDirectory: initialDirectory, + confirmButtonText: confirmButtonText, + ); + + expect(directoryPath, expectedDirectoryPath); + }); + + test('works with no arguments', () async { + when(mock.getDirectoryPath()) + .thenAnswer((_) => Future.value(expectedDirectoryPath)); + + final directoryPath = await getDirectoryPath(); + expect(directoryPath, expectedDirectoryPath); + }); + + test('sets the initial directory', () async { + when(mock.getDirectoryPath(initialDirectory: initialDirectory)) + .thenAnswer((_) => Future.value(expectedDirectoryPath)); - when(mock.openFiles( - acceptedTypeGroups: [typeGroup], - initialDirectory: 'dir', - confirmButtonText: 'load', - )).thenAnswer((_) => Future.value([file])); + final directoryPath = + await getDirectoryPath(initialDirectory: initialDirectory); + expect(directoryPath, expectedDirectoryPath); + }); - final result = await openFiles( - acceptedTypeGroups: [typeGroup], - initialDirectory: 'dir', - confirmButtonText: 'load'); + test('sets the button confirmation label', () async { + when(mock.getDirectoryPath(confirmButtonText: confirmButtonText)) + .thenAnswer((_) => Future.value(expectedDirectoryPath)); - expect(result, [file]); + final directoryPath = + await getDirectoryPath(confirmButtonText: confirmButtonText); + expect(directoryPath, expectedDirectoryPath); + }); }); } From 113c7562861e6511efefb0050e88a51870b458b9 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Fri, 20 Nov 2020 11:10:49 -0600 Subject: [PATCH 149/151] Fix the example's class comments --- .../file_selector/example/lib/get_directory_page.dart | 2 +- .../file_selector/example/lib/open_image_page.dart | 2 +- .../file_selector/example/lib/open_multiple_images_page.dart | 2 +- .../file_selector/file_selector/example/lib/open_text_page.dart | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/get_directory_page.dart b/packages/file_selector/file_selector/example/lib/get_directory_page.dart index 5e4378a8fcb1..36058da9e9aa 100644 --- a/packages/file_selector/file_selector/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector/example/lib/get_directory_page.dart @@ -1,7 +1,7 @@ import 'package:file_selector/file_selector.dart'; import 'package:flutter/material.dart'; -/// Screen that shows an example of openFile(s) +/// Screen that shows an example of getDirectoryPath class GetDirectoryPage extends StatelessWidget { void _getDirectoryPath(BuildContext context) async { final String initialDirectory = '~/Desktop'; diff --git a/packages/file_selector/file_selector/example/lib/open_image_page.dart b/packages/file_selector/file_selector/example/lib/open_image_page.dart index 1428aa2141a7..2821635fb30b 100644 --- a/packages/file_selector/file_selector/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_image_page.dart @@ -3,7 +3,7 @@ import 'package:flutter/foundation.dart'; import 'package:file_selector/file_selector.dart'; import 'package:flutter/material.dart'; -/// Screen that shows an example of openFile(s) +/// Screen that shows an example of openFiles class OpenImagePage extends StatelessWidget { void _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( diff --git a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart index a037252def1f..d27579e5bcc0 100644 --- a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart @@ -3,7 +3,7 @@ import 'package:flutter/foundation.dart'; import 'package:file_selector/file_selector.dart'; import 'package:flutter/material.dart'; -/// Screen that shows an example of openFile(s) +/// Screen that shows an example of openFiles class OpenMultipleImagesPage extends StatelessWidget { void _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( diff --git a/packages/file_selector/file_selector/example/lib/open_text_page.dart b/packages/file_selector/file_selector/example/lib/open_text_page.dart index b1b7947a7af0..4cb3064046fd 100644 --- a/packages/file_selector/file_selector/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_text_page.dart @@ -1,7 +1,7 @@ import 'package:file_selector/file_selector.dart'; import 'package:flutter/material.dart'; -/// Screen that shows an example of openFile(s) +/// Screen that shows an example of openFile class OpenTextPage extends StatelessWidget { void _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( From 8fb1fcc114b081dca9d8c1e9ee10a1391501fe9f Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Fri, 20 Nov 2020 11:21:18 -0600 Subject: [PATCH 150/151] Change button's label --- .../file_selector/example/lib/get_directory_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/file_selector/file_selector/example/lib/get_directory_page.dart b/packages/file_selector/file_selector/example/lib/get_directory_page.dart index 36058da9e9aa..f966e79baff0 100644 --- a/packages/file_selector/file_selector/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector/example/lib/get_directory_page.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; class GetDirectoryPage extends StatelessWidget { void _getDirectoryPath(BuildContext context) async { final String initialDirectory = '~/Desktop'; - final String confirmButtonText = 'Choose a cool directory'; + final String confirmButtonText = 'Choose'; final String directoryPath = await getDirectoryPath( initialDirectory: initialDirectory, confirmButtonText: confirmButtonText); From ea11561071611f728c65cd9588b649ba69c6898e Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Fri, 20 Nov 2020 11:24:51 -0600 Subject: [PATCH 151/151] Address nits --- .../file_selector/example/lib/get_directory_page.dart | 5 ++--- .../file_selector/example/lib/open_multiple_images_page.dart | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/get_directory_page.dart b/packages/file_selector/file_selector/example/lib/get_directory_page.dart index f966e79baff0..2afc58f246a4 100644 --- a/packages/file_selector/file_selector/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector/example/lib/get_directory_page.dart @@ -4,11 +4,10 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of getDirectoryPath class GetDirectoryPage extends StatelessWidget { void _getDirectoryPath(BuildContext context) async { - final String initialDirectory = '~/Desktop'; final String confirmButtonText = 'Choose'; final String directoryPath = await getDirectoryPath( - initialDirectory: initialDirectory, - confirmButtonText: confirmButtonText); + confirmButtonText: confirmButtonText, + ); await showDialog( context: context, builder: (context) => TextDisplay(directoryPath), diff --git a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart index d27579e5bcc0..7a27a0c0715f 100644 --- a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart @@ -7,10 +7,11 @@ import 'package:flutter/material.dart'; class OpenMultipleImagesPage extends StatelessWidget { void _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( - label: 'images', + label: 'JPEGs', extensions: ['jpg', 'jpeg'], ); final XTypeGroup pngTypeGroup = XTypeGroup( + label: 'PNGs', extensions: ['png'], ); final List files = await openFiles(acceptedTypeGroups: [