diff --git a/packages/file_selector/file_selector/AUTHORS b/packages/file_selector/file_selector/AUTHORS index dbf9d190931..94743a9a64a 100644 --- a/packages/file_selector/file_selector/AUTHORS +++ b/packages/file_selector/file_selector/AUTHORS @@ -63,3 +63,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +TowaYamashita diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 63978336185..ed7b213b568 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.8.4+3 +* Improves API docs and examples. * Minor fixes for new analysis options. ## 0.8.4+2 diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index 89cac1e6fd5..f5c1de8afeb 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -1,5 +1,7 @@ # 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. @@ -30,25 +32,48 @@ 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]); +final XTypeGroup typeGroup = XTypeGroup( + label: 'images', + extensions: ['jpg', 'png'], +); +final XFile? 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]); +final XTypeGroup jpgsTypeGroup = XTypeGroup( + label: 'JPEGs', + extensions: ['jpg', 'jpeg'], +); +final XTypeGroup pngTypeGroup = XTypeGroup( + label: 'PNGs', + extensions: ['png'], +); +final List files = await openFiles(acceptedTypeGroups: [ + jpgsTypeGroup, + pngTypeGroup, +]); ``` #### 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); +const String fileName = 'suggested_name.txt'; +final String? path = await getSavePath(suggestedName: fileName); +if (path == null) { + // Operation was canceled by the user. + return; +} + +final Uint8List fileData = Uint8List.fromList('Hello World!'.codeUnits); +const String mimeType = 'text/plain'; +final XFile textFile = + XFile.fromData(fileData, mimeType: mimeType, name: fileName); +await textFile.saveTo(path); ``` [example]:./example diff --git a/packages/file_selector/file_selector/example/build.excerpt.yaml b/packages/file_selector/file_selector/example/build.excerpt.yaml new file mode 100644 index 00000000000..e317efa11cb --- /dev/null +++ b/packages/file_selector/file_selector/example/build.excerpt.yaml @@ -0,0 +1,15 @@ +targets: + $default: + sources: + include: + - lib/** + # Some default includes that aren't really used here but will prevent + # false-negative warnings: + - $package$ + - lib/$lib$ + exclude: + - '**/.*/**' + - '**/build/**' + builders: + code_excerpter|code_excerpter: + enabled: true diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 34f5857ab0b..d05e80f1b75 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:example/get_directory_page.dart'; -import 'package:example/home_page.dart'; -import 'package:example/open_image_page.dart'; -import 'package:example/open_multiple_images_page.dart'; -import 'package:example/open_text_page.dart'; -import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; +import 'get_directory_page.dart'; +import 'home_page.dart'; +import 'open_image_page.dart'; +import 'open_multiple_images_page.dart'; +import 'open_text_page.dart'; +import 'save_text_page.dart'; + void main() { runApp(const MyApp()); } 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 e520ffb402a..4fb06864c57 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 @@ -14,17 +14,18 @@ class OpenImagePage extends StatelessWidget { const OpenImagePage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { + // #docregion SingleOpen final XTypeGroup typeGroup = XTypeGroup( label: 'images', extensions: ['jpg', 'png'], ); - final List files = - await openFiles(acceptedTypeGroups: [typeGroup]); - if (files.isEmpty) { + final XFile? file = + await openFile(acceptedTypeGroups: [typeGroup]); + // #enddocregion SingleOpen + if (file == null) { // Operation was canceled by the user. return; } - 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 index e2d21c7f04d..ac3d66fc955 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 @@ -14,6 +14,7 @@ class OpenMultipleImagesPage extends StatelessWidget { const OpenMultipleImagesPage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { + // #docregion MultiOpen final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', extensions: ['jpg', 'jpeg'], @@ -26,6 +27,7 @@ class OpenMultipleImagesPage extends StatelessWidget { jpgsTypeGroup, pngTypeGroup, ]); + // #enddocregion MultiOpen if (files.isEmpty) { // Operation was canceled by the user. return; 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 be48a434282..057925ed43c 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 @@ -4,6 +4,7 @@ import 'package:file_selector/file_selector.dart'; import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; /// Screen that shows an example of openFile class OpenTextPage extends StatelessWidget { @@ -15,8 +16,15 @@ class OpenTextPage extends StatelessWidget { label: 'text', extensions: ['txt', 'json'], ); - final XFile? file = - await openFile(acceptedTypeGroups: [typeGroup]); + // This demonstrates using an initial directory for the prompt, which should + // only be done in cases where the application can likely predict where the + // file would be. In most cases, this parameter should not be provided. + final String initialDirectory = + (await getApplicationDocumentsDirectory()).path; + final XFile? file = await openFile( + acceptedTypeGroups: [typeGroup], + initialDirectory: initialDirectory, + ); if (file == null) { // Operation was canceled by the user. return; diff --git a/packages/file_selector/file_selector/example/lib/readme_standalone_excerpts.dart b/packages/file_selector/file_selector/example/lib/readme_standalone_excerpts.dart new file mode 100644 index 00000000000..c67c93fa63f --- /dev/null +++ b/packages/file_selector/file_selector/example/lib/readme_standalone_excerpts.dart @@ -0,0 +1,57 @@ +// 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. + +// This file exists solely to host compiled excerpts for README.md, and is not +// intended for use as an actual example application. + +// ignore_for_file: public_member_api_docs + +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import +import 'dart:typed_data'; + +import 'package:file_selector/file_selector.dart'; +import 'package:flutter/material.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('README snippet app'), + ), + body: const Text('See example in main.dart'), + ), + ); + } + + Future saveFile() async { + // #docregion Save + const String fileName = 'suggested_name.txt'; + final String? path = await getSavePath(suggestedName: fileName); + if (path == null) { + // Operation was canceled by the user. + return; + } + + final Uint8List fileData = Uint8List.fromList('Hello World!'.codeUnits); + const String mimeType = 'text/plain'; + final XFile textFile = + XFile.fromData(fileData, mimeType: mimeType, name: fileName); + await textFile.saveTo(path); + // #enddocregion Save + } +} 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 index b0317844ec3..257add5b6de 100644 --- a/packages/file_selector/file_selector/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/save_text_page.dart @@ -5,6 +5,7 @@ import 'dart:typed_data'; import 'package:file_selector/file_selector.dart'; import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; /// Page for showing an example of saving with file_selector class SaveTextPage extends StatelessWidget { @@ -15,17 +16,27 @@ class SaveTextPage extends StatelessWidget { final TextEditingController _contentController = TextEditingController(); Future _saveFile() async { - final String? path = await getSavePath(); + final String fileName = _nameController.text; + // This demonstrates using an initial directory for the prompt, which should + // only be done in cases where the application can likely predict where the + // file will be saved. In most cases, this parameter should not be provided. + final String initialDirectory = + (await getApplicationDocumentsDirectory()).path; + final String? path = await getSavePath( + initialDirectory: initialDirectory, + suggestedName: fileName, + ); if (path == null) { // Operation was canceled by the user. return; } + final String text = _contentController.text; - final String fileName = _nameController.text; final Uint8List fileData = Uint8List.fromList(text.codeUnits); const String fileMimeType = 'text/plain'; final XFile textFile = XFile.fromData(fileData, mimeType: fileMimeType, name: fileName); + await textFile.saveTo(path); } diff --git a/packages/file_selector/file_selector/example/pubspec.yaml b/packages/file_selector/file_selector/example/pubspec.yaml index 531f4790afd..011d95874ae 100644 --- a/packages/file_selector/file_selector/example/pubspec.yaml +++ b/packages/file_selector/file_selector/example/pubspec.yaml @@ -1,4 +1,4 @@ -name: example +name: file_selector_example description: A new Flutter project. publish_to: none @@ -17,8 +17,10 @@ dependencies: path: ../ flutter: sdk: flutter + path_provider: ^2.0.9 dev_dependencies: + build_runner: ^2.1.10 flutter_test: sdk: flutter diff --git a/packages/file_selector/file_selector/example/windows/flutter/generated_plugins.cmake b/packages/file_selector/file_selector/example/windows/flutter/generated_plugins.cmake index 63eda9b7b59..a423a02476a 100644 --- a/packages/file_selector/file_selector/example/windows/flutter/generated_plugins.cmake +++ b/packages/file_selector/file_selector/example/windows/flutter/generated_plugins.cmake @@ -6,6 +6,9 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_windows ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -14,3 +17,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index c2803d60c97..322ae6cda48 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -9,7 +9,24 @@ import 'package:file_selector_platform_interface/file_selector_platform_interfac export 'package:file_selector_platform_interface/file_selector_platform_interface.dart' show XFile, XTypeGroup; -/// Open file dialog for loading files and return a file path +/// Opens a file selection dialog and returns the path chosen by the user. +/// +/// [acceptedTypeGroups] is a list of file type groups that can be selected in +/// the dialog. How this is displayed depends on the pltaform, for example: +/// - On Windows and Linux, each group will be an entry in a list of filter +/// options. +/// - On macOS, the union of all types allowed by all of the groups will be +/// allowed. +/// +/// [initialDirectory] is the full path to the directory that will be displayed +/// when the dialog is opened. When not provided, the platform will pick an +/// initial location. This is ignored on the Web platform. +/// +/// [confirmButtonText] is the text in the confirmation button of the dialog. +/// When not provided, the default OS label is used (for example, "Open"). +/// This is ignored on the Web platform. +/// +/// Returns `null` if the user cancels the operation. Future openFile({ List acceptedTypeGroups = const [], String? initialDirectory, @@ -21,7 +38,24 @@ Future openFile({ confirmButtonText: confirmButtonText); } -/// Open file dialog for loading files and return a list of file paths +/// Opens a file selection dialog and returns the list of paths chosen by the +/// user. +/// +/// [acceptedTypeGroups] is a list of file type groups that can be selected in +/// the dialog. How this is displayed depends on the pltaform, for example: +/// - On Windows and Linux, each group will be an entry in a list of filter +/// options. +/// - On macOS, the union of all types allowed by all of the groups will be +/// allowed. +/// +/// [initialDirectory] is the full path to the directory that will be displayed +/// when the dialog is opened. When not provided, the platform will pick an +/// initial location. +/// +/// [confirmButtonText] is the text in the confirmation button of the dialog. +/// When not provided, the default OS label is used (for example, "Open"). +/// +/// Returns an empty list if the user cancels the operation. Future> openFiles({ List acceptedTypeGroups = const [], String? initialDirectory, @@ -33,7 +67,25 @@ Future> openFiles({ confirmButtonText: confirmButtonText); } -/// Saves File to user's file system +/// Opens a save dialog and returns the target path chosen by the user. +/// +/// [acceptedTypeGroups] is a list of file type groups that can be selected in +/// the dialog. How this is displayed depends on the pltaform, for example: +/// - On Windows and Linux, each group will be an entry in a list of filter +/// options. +/// - On macOS, the union of all types allowed by all of the groups will be +/// allowed. +/// +/// [initialDirectory] is the full path to the directory that will be displayed +/// when the dialog is opened. When not provided, the platform will pick an +/// initial location. +/// +/// [suggestedName] is initial value of file name. +/// +/// [confirmButtonText] is the text in the confirmation button of the dialog. +/// When not provided, the default OS label is used (for example, "Save"). +/// +/// Returns `null` if the user cancels the operation. Future getSavePath({ List acceptedTypeGroups = const [], String? initialDirectory, @@ -47,7 +99,17 @@ Future getSavePath({ confirmButtonText: confirmButtonText); } -/// Gets a directory path from a user's file system +/// Opens a directory selection dialog and returns the path chosen by the user. +/// This always returns `null` on the web. +/// +/// [initialDirectory] is the full path to the directory that will be displayed +/// when the dialog is opened. When not provided, the platform will pick an +/// initial location. +/// +/// [confirmButtonText] is the text in the confirmation button of the dialog. +/// When not provided, the default OS label is used (for example, "Open"). +/// +/// Returns `null` if the user cancels the operation. Future getDirectoryPath({ String? initialDirectory, String? confirmButtonText, diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 1c502c055c9..12a1fbc4778 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.4+2 +version: 0.8.4+3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/script/configs/temp_exclude_excerpt.yaml b/script/configs/temp_exclude_excerpt.yaml index bd73880ee5e..dec99ee6edd 100644 --- a/script/configs/temp_exclude_excerpt.yaml +++ b/script/configs/temp_exclude_excerpt.yaml @@ -7,7 +7,6 @@ # https://github.com/flutter/flutter/issues/102679 - camera_web - espresso -- file_selector/file_selector - google_maps_flutter/google_maps_flutter - google_sign_in/google_sign_in - google_sign_in_web