From ca8ed1e12b0297d2de94dbe0c332657bc680e6ff Mon Sep 17 00:00:00 2001 From: Jakub Walusiak Date: Tue, 12 Jul 2022 18:43:55 +0200 Subject: [PATCH 1/5] [image_picker_android] Name picked files to match the original filenames where possible. --- .../plugins/imagepicker/FileUtils.java | 74 ++++++++------- .../plugins/imagepicker/FileUtilTest.java | 72 +++++++++++++-- .../example/android/app/build.gradle | 2 + .../ImagePickerPickTest.java | 43 +++++++++ .../android/app/src/debug/AndroidManifest.xml | 12 +++ .../DriverExtensionActivity.java | 14 +++ .../DummyContentProvider.java | 64 +++++++++++++ .../app/src/main/res/raw/flutter_logo.png | Bin 0 -> 7231 bytes .../example/lib/main.dart | 84 ++++++++++++------ 9 files changed, 297 insertions(+), 68 deletions(-) create mode 100644 packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/ImagePickerPickTest.java create mode 100644 packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DriverExtensionActivity.java create mode 100644 packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DummyContentProvider.java create mode 100644 packages/image_picker/image_picker_android/example/android/app/src/main/res/raw/flutter_logo.png diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java index 1f51a226c7e2..c31e88a442f1 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java @@ -25,55 +25,46 @@ import android.content.ContentResolver; import android.content.Context; +import android.database.Cursor; import android.net.Uri; +import android.provider.MediaStore; import android.webkit.MimeTypeMap; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; +import java.util.UUID; class FileUtils { + @SuppressWarnings("IOStreamConstructor") String getPathFromUri(final Context context, final Uri uri) { - File file = null; - InputStream inputStream = null; - OutputStream outputStream = null; - boolean success = false; - try { - String extension = getImageExtension(context, uri); - inputStream = context.getContentResolver().openInputStream(uri); - file = File.createTempFile("image_picker", extension, context.getCacheDir()); - file.deleteOnExit(); - outputStream = new FileOutputStream(file); - if (inputStream != null) { - copy(inputStream, outputStream); - success = true; - } - } catch (IOException ignored) { - } finally { - try { - if (inputStream != null) inputStream.close(); - } catch (IOException ignored) { + try (InputStream inputStream = context.getContentResolver().openInputStream(uri)) { + String uuid = UUID.randomUUID().toString(); + File targetDirectory = new File(context.getCacheDir(), uuid); + targetDirectory.mkdir(); + // TODO(SynSzakala) according to the docs, `deleteOnExit` does not work reliably on Android; we should preferably + // just clear the picked files after the app startup. + targetDirectory.deleteOnExit(); + String fileName = getImageName(context, uri); + if (fileName == null) { + fileName = uuid + getImageExtension(context, uri); } - try { - if (outputStream != null) outputStream.close(); - } catch (IOException ignored) { - // If closing the output stream fails, we cannot be sure that the - // target file was written in full. Flushing the stream merely moves - // the bytes into the OS, not necessarily to the file. - success = false; + File file = new File(targetDirectory, fileName); + try (OutputStream outputStream = new FileOutputStream(file)) { + copy(inputStream, outputStream); + return file.getPath(); } + } catch (IOException e) { + // If closing the output stream fails, we cannot be sure that the + // target file was written in full. Flushing the stream merely moves + // the bytes into the OS, not necessarily to the file. + return null; } - return success ? file.getPath() : null; } /** @return extension of image with dot, or default .jpg if it none. */ private static String getImageExtension(Context context, Uri uriImage) { - String extension = null; + String extension; try { - String imagePath = uriImage.getPath(); if (uriImage.getScheme().equals(ContentResolver.SCHEME_CONTENT)) { final MimeTypeMap mime = MimeTypeMap.getSingleton(); extension = mime.getExtensionFromMimeType(context.getContentResolver().getType(uriImage)); @@ -94,6 +85,21 @@ private static String getImageExtension(Context context, Uri uriImage) { return "." + extension; } + /** @return name of the image provided by ContentResolver; this may be null. */ + private static String getImageName(Context context, Uri uriImage) { + try (Cursor cursor = queryImageName(context, uriImage)) { + if (cursor == null) return null; + cursor.moveToFirst(); + return cursor.getString(0); + } + } + + private static Cursor queryImageName(Context context, Uri uriImage) { + return context + .getContentResolver() + .query(uriImage, new String[] {MediaStore.MediaColumns.DISPLAY_NAME}, null, null, null); + } + private static void copy(InputStream in, OutputStream out) throws IOException { final byte[] buffer = new byte[4 * 1024]; int bytesRead; diff --git a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java index 32e3ebc6183d..905f62566053 100644 --- a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java +++ b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java @@ -8,17 +8,21 @@ import static org.junit.Assert.assertTrue; import static org.robolectric.Shadows.shadowOf; +import android.content.ContentProvider; +import android.content.ContentValues; import android.content.Context; +import android.database.Cursor; +import android.database.MatrixCursor; import android.net.Uri; +import android.provider.MediaStore; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.test.core.app.ApplicationProvider; -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; +import java.io.*; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.shadows.ShadowContentResolver; @@ -63,4 +67,62 @@ public void FileUtil_getImageExtension() throws IOException { String path = fileUtils.getPathFromUri(context, uri); assertTrue(path.endsWith(".jpg")); } + + @Test + public void FileUtil_getImageName() throws IOException { + Uri uri = Uri.parse("content://dummy/dummy.png"); + Robolectric.buildContentProvider(MockContentProvider.class).create("dummy"); + shadowContentResolver.registerInputStream( + uri, new ByteArrayInputStream("imageStream".getBytes(UTF_8))); + String path = fileUtils.getPathFromUri(context, uri); + assertTrue(path.endsWith("dummy.png")); + } + + private static class MockContentProvider extends ContentProvider { + + @Override + public boolean onCreate() { + return true; + } + + @Nullable + @Override + public Cursor query( + @NonNull Uri uri, + @Nullable String[] projection, + @Nullable String selection, + @Nullable String[] selectionArgs, + @Nullable String sortOrder) { + MatrixCursor cursor = new MatrixCursor(new String[] {MediaStore.MediaColumns.DISPLAY_NAME}); + cursor.addRow(new Object[] {"dummy.png"}); + return cursor; + } + + @Nullable + @Override + public String getType(@NonNull Uri uri) { + return "image/png"; + } + + @Nullable + @Override + public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { + return null; + } + + @Override + public int delete( + @NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { + return 0; + } + + @Override + public int update( + @NonNull Uri uri, + @Nullable ContentValues values, + @Nullable String selection, + @Nullable String[] selectionArgs) { + return 0; + } + } } diff --git a/packages/image_picker/image_picker_android/example/android/app/build.gradle b/packages/image_picker/image_picker_android/example/android/app/build.gradle index 31d8c82a0a9d..f8487c7959f1 100755 --- a/packages/image_picker/image_picker_android/example/android/app/build.gradle +++ b/packages/image_picker/image_picker_android/example/android/app/build.gradle @@ -63,5 +63,7 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + implementation project(':image_picker_android') + implementation project(':espresso') api 'androidx.test:core:1.4.0' } diff --git a/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/ImagePickerPickTest.java b/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/ImagePickerPickTest.java new file mode 100644 index 000000000000..8b7ae11d5c2d --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/ImagePickerPickTest.java @@ -0,0 +1,43 @@ +// 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. + +package io.flutter.plugins.imagepickerexample; + +import static androidx.test.espresso.flutter.EspressoFlutter.onFlutterWidget; +import static androidx.test.espresso.flutter.action.FlutterActions.click; +import static androidx.test.espresso.flutter.assertion.FlutterAssertions.matches; +import static androidx.test.espresso.flutter.matcher.FlutterMatchers.withText; +import static androidx.test.espresso.flutter.matcher.FlutterMatchers.withValueKey; +import static androidx.test.espresso.intent.Intents.intended; +import static androidx.test.espresso.intent.Intents.intending; +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.Intent; +import android.net.Uri; +import androidx.test.espresso.intent.rule.IntentsTestRule; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; + +public class ImagePickerPickTest { + + @Rule public TestRule rule = new IntentsTestRule<>(DriverExtensionActivity.class); + + @Test + @Ignore("Doesn't run in Firebase Test Lab: https://github.com/flutter/flutter/issues/94748") + public void imageIsPickedWithOriginalName() { + Instrumentation.ActivityResult result = + new Instrumentation.ActivityResult( + Activity.RESULT_OK, new Intent().setData(Uri.parse("content://dummy/dummy.png"))); + intending(hasAction(Intent.ACTION_GET_CONTENT)).respondWith(result); + onFlutterWidget(withValueKey("image_picker_example_from_gallery")).perform(click()); + onFlutterWidget(withText("PICK")).perform(click()); + intended(hasAction(Intent.ACTION_GET_CONTENT)); + onFlutterWidget(withValueKey("image_picker_example_picked_image_name")) + .check(matches(withText("dummy.png"))); + } +} diff --git a/packages/image_picker/image_picker_android/example/android/app/src/debug/AndroidManifest.xml b/packages/image_picker/image_picker_android/example/android/app/src/debug/AndroidManifest.xml index 6f85cefded34..317af1d1a371 100644 --- a/packages/image_picker/image_picker_android/example/android/app/src/debug/AndroidManifest.xml +++ b/packages/image_picker/image_picker_android/example/android/app/src/debug/AndroidManifest.xml @@ -13,5 +13,17 @@ android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> + + + diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DriverExtensionActivity.java b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DriverExtensionActivity.java new file mode 100644 index 000000000000..c9700c602f3b --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DriverExtensionActivity.java @@ -0,0 +1,14 @@ +package io.flutter.plugins.imagepickerexample; + +import androidx.annotation.NonNull; +import io.flutter.embedding.android.FlutterActivity; +import org.jetbrains.annotations.NotNull; + +public class DriverExtensionActivity extends FlutterActivity { + @NonNull + @NotNull + @Override + public String getDartEntrypointFunctionName() { + return "appMain"; + } +} diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DummyContentProvider.java b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DummyContentProvider.java new file mode 100644 index 000000000000..dd04e4b6c322 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DummyContentProvider.java @@ -0,0 +1,64 @@ +package io.flutter.plugins.imagepickerexample; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.res.AssetFileDescriptor; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.net.Uri; +import android.provider.MediaStore; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class DummyContentProvider extends ContentProvider { + @Override + public boolean onCreate() { + return true; + } + + @Nullable + @Override + public AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode) { + return getContext().getResources().openRawResourceFd(R.raw.flutter_logo); + } + + @Nullable + @Override + public Cursor query( + @NonNull Uri uri, + @Nullable String[] projection, + @Nullable String selection, + @Nullable String[] selectionArgs, + @Nullable String sortOrder) { + MatrixCursor cursor = new MatrixCursor(new String[] {MediaStore.MediaColumns.DISPLAY_NAME}); + cursor.addRow(new Object[] {"dummy.png"}); + return cursor; + } + + @Nullable + @Override + public String getType(@NonNull Uri uri) { + return "image/png"; + } + + @Nullable + @Override + public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { + return null; + } + + @Override + public int delete( + @NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { + return 0; + } + + @Override + public int update( + @NonNull Uri uri, + @Nullable ContentValues values, + @Nullable String selection, + @Nullable String[] selectionArgs) { + return 0; + } +} diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/res/raw/flutter_logo.png b/packages/image_picker/image_picker_android/example/android/app/src/main/res/raw/flutter_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..cfc0ee42f375540248aeb0710a9c89144469b338 GIT binary patch literal 7231 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 literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_android/example/lib/main.dart b/packages/image_picker/image_picker_android/example/lib/main.dart index 212e064cc6e5..03a4615efaaf 100755 --- a/packages/image_picker/image_picker_android/example/lib/main.dart +++ b/packages/image_picker/image_picker_android/example/lib/main.dart @@ -9,9 +9,15 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_driver/driver_extension.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'package:video_player/video_player.dart'; +void appMain() { + enableFlutterDriverExtension(); + main(); +} + void main() { runApp(const MyApp()); } @@ -80,17 +86,23 @@ class _MyHomePageState extends State { } } - Future _onImageButtonPressed(ImageSource source, - {BuildContext? context, bool isMultiImage = false}) async { + Future _onImageButtonPressed( + BuildContext context, { + required ImageSource source, + bool isMultiImage = false, + }) async { if (_controller != null) { await _controller!.setVolume(0.0); } if (isVideo) { final XFile? file = await _picker.getVideo( source: source, maxDuration: const Duration(seconds: 10)); + if (file != null) { + _showPickedSnackBar(context, [file]); + } await _playVideo(file); } else if (isMultiImage) { - await _displayPickImageDialog(context!, + await _displayPickImageDialog(context, (double? maxWidth, double? maxHeight, int? quality) async { try { final List? pickedFileList = await _picker.getMultiImage( @@ -98,17 +110,16 @@ class _MyHomePageState extends State { maxHeight: maxHeight, imageQuality: quality, ); - setState(() { - _imageFileList = pickedFileList; - }); + if (pickedFileList != null) { + _showPickedSnackBar(context, pickedFileList); + } + setState(() => _imageFileList = pickedFileList); } catch (e) { - setState(() { - _pickImageError = e; - }); + setState(() => _pickImageError = e); } }); } else { - await _displayPickImageDialog(context!, + await _displayPickImageDialog(context, (double? maxWidth, double? maxHeight, int? quality) async { try { final XFile? pickedFile = await _picker.getImage( @@ -117,13 +128,12 @@ class _MyHomePageState extends State { maxHeight: maxHeight, imageQuality: quality, ); - setState(() { - _setImageFileListFromFile(pickedFile); - }); + if (pickedFile != null) { + _showPickedSnackBar(context, [pickedFile]); + } + setState(() => _setImageFileListFromFile(pickedFile)); } catch (e) { - setState(() { - _pickImageError = e; - }); + setState(() => _pickImageError = e); } }); } @@ -183,13 +193,21 @@ class _MyHomePageState extends State { child: ListView.builder( key: UniqueKey(), itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), + final XFile image = _imageFileList![index]; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(image.name, + key: const Key('image_picker_example_picked_image_name')), + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(image.path) + : Image.file(File(image.path)), + ), + ], ); }, itemCount: _imageFileList!.length, @@ -283,9 +301,10 @@ class _MyHomePageState extends State { Semantics( label: 'image_picker_example_from_gallery', child: FloatingActionButton( + key: const Key('image_picker_example_from_gallery'), onPressed: () { isVideo = false; - _onImageButtonPressed(ImageSource.gallery, context: context); + _onImageButtonPressed(context, source: ImageSource.gallery); }, heroTag: 'image0', tooltip: 'Pick Image from gallery', @@ -298,8 +317,8 @@ class _MyHomePageState extends State { onPressed: () { isVideo = false; _onImageButtonPressed( - ImageSource.gallery, - context: context, + context, + source: ImageSource.gallery, isMultiImage: true, ); }, @@ -313,7 +332,7 @@ class _MyHomePageState extends State { child: FloatingActionButton( onPressed: () { isVideo = false; - _onImageButtonPressed(ImageSource.camera, context: context); + _onImageButtonPressed(context, source: ImageSource.camera); }, heroTag: 'image2', tooltip: 'Take a Photo', @@ -326,7 +345,7 @@ class _MyHomePageState extends State { backgroundColor: Colors.red, onPressed: () { isVideo = true; - _onImageButtonPressed(ImageSource.gallery); + _onImageButtonPressed(context, source: ImageSource.gallery); }, heroTag: 'video0', tooltip: 'Pick Video from gallery', @@ -339,7 +358,7 @@ class _MyHomePageState extends State { backgroundColor: Colors.red, onPressed: () { isVideo = true; - _onImageButtonPressed(ImageSource.camera); + _onImageButtonPressed(context, source: ImageSource.camera); }, heroTag: 'video1', tooltip: 'Take a Video', @@ -417,6 +436,13 @@ class _MyHomePageState extends State { ); }); } + + void _showPickedSnackBar(BuildContext context, List files) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Picked: ${files.map((XFile it) => it.name).join(',')}'), + duration: const Duration(seconds: 2), + )); + } } typedef OnPickImageCallback = void Function( From 59266d7f9348f6865efdaec85a6c5f83837153f2 Mon Sep 17 00:00:00 2001 From: Jakub Walusiak Date: Tue, 12 Jul 2022 18:45:18 +0200 Subject: [PATCH 2/5] [image_picker_android] Update CHANGELOG.md --- packages/image_picker/image_picker_android/CHANGELOG.md | 4 ++++ packages/image_picker/image_picker_android/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 664be36f880f..0f066091695b 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.5+2 + +* Fixes names of picked files to match original filenames where possible. + ## 0.8.5+1 * Switches to an internal method channel implementation. diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index 8cbaaac71daf..4ede19411e0f 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_android description: Android implementation of the image_picker plugin. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5+1 +version: 0.8.5+2 environment: sdk: ">=2.14.0 <3.0.0" From d217cb517f7d3b1acf191a8c4a4ce7cae09f73b9 Mon Sep 17 00:00:00 2001 From: Jakub Walusiak Date: Tue, 12 Jul 2022 18:58:44 +0200 Subject: [PATCH 3/5] [image_picker_android] Add license blocks --- .../plugins/imagepickerexample/DriverExtensionActivity.java | 4 ++++ .../plugins/imagepickerexample/DummyContentProvider.java | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DriverExtensionActivity.java b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DriverExtensionActivity.java index c9700c602f3b..da95b0a5dc00 100644 --- a/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DriverExtensionActivity.java +++ b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DriverExtensionActivity.java @@ -1,3 +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. + package io.flutter.plugins.imagepickerexample; import androidx.annotation.NonNull; diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DummyContentProvider.java b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DummyContentProvider.java index dd04e4b6c322..907762272f4c 100644 --- a/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DummyContentProvider.java +++ b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DummyContentProvider.java @@ -1,3 +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. + package io.flutter.plugins.imagepickerexample; import android.content.ContentProvider; From 36b8d2dadc914ed55d8f69b67ff68bb86390273c Mon Sep 17 00:00:00 2001 From: Jakub Walusiak Date: Thu, 3 Nov 2022 16:47:29 +0100 Subject: [PATCH 4/5] [image_picker_android] Clear imports, document FileUtils.getPathFromUri --- .../plugins/imagepicker/FileUtils.java | 25 +++++++++++++----- .../plugins/imagepicker/FileUtilTest.java | 6 ++++- .../DriverExtensionActivity.java | 2 -- .../DummyContentProvider.java | 2 +- .../app/src/main/res/raw/flutter_logo.png | Bin 7231 -> 0 bytes .../app/src/main/res/raw/ic_launcher.png | Bin 0 -> 442 bytes .../image_picker_android/example/pubspec.yaml | 4 +-- 7 files changed, 27 insertions(+), 12 deletions(-) delete mode 100644 packages/image_picker/image_picker_android/example/android/app/src/main/res/raw/flutter_logo.png create mode 100755 packages/image_picker/image_picker_android/example/android/app/src/main/res/raw/ic_launcher.png diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java index c31e88a442f1..449480c19d9c 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java @@ -29,12 +29,25 @@ import android.net.Uri; import android.provider.MediaStore; import android.webkit.MimeTypeMap; -import java.io.*; +import io.flutter.Log; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.UUID; class FileUtils { - - @SuppressWarnings("IOStreamConstructor") + /** + * Copies the file from the given content URI to a temporary directory, retaining the original + * file name if possible. + * + *

Each file is placed in its own directory to avoid conflicts according to the following + * scheme: {cacheDir}/{randomUuid}/{fileName} + * + *

If the original file name is unknown, a predefined "image_picker" filename is used and the + * file extension is deduced from the mime type (with fallback to ".jpg" in case of failure). + */ String getPathFromUri(final Context context, final Uri uri) { try (InputStream inputStream = context.getContentResolver().openInputStream(uri)) { String uuid = UUID.randomUUID().toString(); @@ -45,7 +58,8 @@ String getPathFromUri(final Context context, final Uri uri) { targetDirectory.deleteOnExit(); String fileName = getImageName(context, uri); if (fileName == null) { - fileName = uuid + getImageExtension(context, uri); + Log.w("FileUtils", "Cannot get file name for " + uri); + fileName = "image_picker" + getImageExtension(context, uri); } File file = new File(targetDirectory, fileName); try (OutputStream outputStream = new FileOutputStream(file)) { @@ -88,8 +102,7 @@ private static String getImageExtension(Context context, Uri uriImage) { /** @return name of the image provided by ContentResolver; this may be null. */ private static String getImageName(Context context, Uri uriImage) { try (Cursor cursor = queryImageName(context, uriImage)) { - if (cursor == null) return null; - cursor.moveToFirst(); + if (cursor == null || !cursor.moveToFirst() || cursor.getColumnCount() < 1) return null; return cursor.getString(0); } } diff --git a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java index 905f62566053..0ea0173fa954 100644 --- a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java +++ b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java @@ -18,7 +18,11 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.core.app.ApplicationProvider; -import java.io.*; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DriverExtensionActivity.java b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DriverExtensionActivity.java index da95b0a5dc00..b35a6c4b0e49 100644 --- a/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DriverExtensionActivity.java +++ b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DriverExtensionActivity.java @@ -6,11 +6,9 @@ import androidx.annotation.NonNull; import io.flutter.embedding.android.FlutterActivity; -import org.jetbrains.annotations.NotNull; public class DriverExtensionActivity extends FlutterActivity { @NonNull - @NotNull @Override public String getDartEntrypointFunctionName() { return "appMain"; diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DummyContentProvider.java b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DummyContentProvider.java index 907762272f4c..8967318ee977 100644 --- a/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DummyContentProvider.java +++ b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/DummyContentProvider.java @@ -23,7 +23,7 @@ public boolean onCreate() { @Nullable @Override public AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode) { - return getContext().getResources().openRawResourceFd(R.raw.flutter_logo); + return getContext().getResources().openRawResourceFd(R.raw.ic_launcher); } @Nullable diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/res/raw/flutter_logo.png b/packages/image_picker/image_picker_android/example/android/app/src/main/res/raw/flutter_logo.png deleted file mode 100644 index cfc0ee42f375540248aeb0710a9c89144469b338..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7231 zcmX9@bwE?!7oT)DN`q`5poD~k5+bmW93b7Hv~)=-A-##9bc_~|ZcqUcP^r-ZQX->~ zF_HSceSd%KZF}#Y&pqed&pr2Z?|Z4Q3#F&wq5*+G^l+F45(FZF1HUV&D1d9*$oy^K zi~1?d%ohZrWg&j9j4ybkS5hbw%fbcrF2 z`1_Z1F71(u3GB>$8)iKDDA}h`|Gvv0bKeVoV|V>1zrV~6G?DBY-%E;&Nzm1gsMFCM z&odwz>;cye^b4CE;?4vix+{NDJLW4E7qdGKLIS-Ov*o=Ozdlp_k~Y)Q(cqg%2;J)n zW9{?4WjkO6y=G3h*S(+I9A9g-+4xI&>*j*W#5v>NA}l{|hoi^)^@idqJxAp`AP}yg zKX5F?PhfKSM1+h5R8SWWp?yY&5xBHsf`KBnz-S+PGHOs{h#=ZWL?-Q45*yxk+n$6P z1QHxHvVx{jK|r9Z%y0_gH!c-6t|4#*G)mX|EWcrkY@C&L$vrXXv2ltbDPR%F#{nOD zBAQ0DjuaL^OADldKu`DtUC5j0F>*lPpyy0*QlM9m>@^gYGKvhO*1!x`CfrTmX+^pzNbJIrix+OpfN8ztX9gfl zM`vKfNI-PsUtnBpFjB22&!7ONt--3`OJ)EtO|w{V&{&|~Gp?8z49(~p*r5yAp#1}) zH5-N*)yKvS0*xAVNbh z1VZ#5jTJ;wkRvk;AbJCDi7p|GPGFVef*#2nU}8sRCE!6VfnbFMV$Od?EmwxK){~)! z$>%esz|1gqme}P+CVPu-`MZ zI$uwL_M&a3RL-EonACV#!HJlu2u<`iwR+JJBMTN-hfN1gvy74+1frwW-jWD&`r zv(~1>43MC|!gRgf*Zbl>+vz|JWOpVesCu8ae4mCIVDR`S5|ZEwnkY=78p+QJ@8>!O zT?vrueG=blZ=lm&22OYw&x#-)|L2ApK1dGj$O7w56=a6NX01QII>@0M0D(&Oh0&F4 zFf*bvn3LudQjpY}YZD%t6i73oca_ygFfpaHAr4Iz;t;$x!TDKCl87b>Fr_BHBc{2$ zeOwD&$pO2WELfp7M`WRXScwKQ`Tx{+{S*u__D3BT82mS6|Gc^25+s9oS#BbGl@%>o zcUJ71Y6h~2UEziYVaV5{6-y#4#HU^CDrgKVtkf-)_|Vp(5KLriIPp(b;d>P3UDTQz z51vbt$m=I5>Vl#V1=deYRRL2_g0nLE5e=p@ln>x$WGQX$(_$ujqw~2p|CX%tSk-y- zdsM|=DDY^GEeq#Eb;bc`$D)*ffSA-sRYTwuxaD~v*K)AWpS%~H|z53lPyGCBe z*>ZZl4EUaozLhXulaG*yohk>}7(0=XiB_e@+|oZFfORwU1=Fi3!3qaNe8h6%Xz0xlm1-?DP%@sDm_$0`4Ux!bh&-JP*-l~@?%z12D zKCUyS@{Xe`k)m_Dwd2k7aFuJstN)U*+_xF3eC?(W6v+ak_0Bm#g3Vfal!<9p(FeP{ zVpuJor(j31N@o;_)ZfNE5@}L-$hSe?%ztBA1+Gbk)CMTP1=Atr6T6)@u2cs&eWmI5 z>_aiJvP3Ef>IOQ229gO3qT1{lcUKZKos#rIm_HI;+M7KxSz$tr&&>H^Ld38fx~8SwK{&<*fol zN=j#FRK3hmj^MN*n&OfHV&M@ojPiq$_b^&0QHs7=UV`Ur$4c zENaw8x)tZ>=s3@je1N14F%)?ME)=-CPP=DE$rhL9F_b@aqr#7XD~8jP%55BQXr4P- zcFmQ{xwmNvuU{tAQ=6pTsMkPFsU3!}U$De)`lCNI zn`hR$HFI1eWi=P>VVl+Gs3ZstP1dJR!dL^Oro#iX=t;p&mY!K)+)CHg#ov@WMqwX* zDr&ogckP}N#^u#R42lI_DHdyVkLhUuWng9jD`Zz{2$l(6c@U1P-g9!kCC#asWpFoc zZ#d*R7Js00WmX>TD6lZmhK*myZL|W)kSuC9+n6J^Ju^?ia*eyFoh^3PcQh=iXlXQb zupvXsIw`<%;7j7rlLCf-{Y7sByZMSg^FS(z=A&8VtE%#wWhZ>|B7B*zRDo4UOp%NwrJVq@md$yznhT53MWIi5$-)b95TCo zv_F2}hZdRC3}X@HJBr%~w_B7P%a+c~4&?`Ce%l*ey3XS366g}f7#qq z^xnw6bCH`1)fA-b6?>-O7Ya`_k5ZC8!OnQgd93-7Rb*RE7f&H~Mmsk>+ar<16KLPlhS0H_d%JUQ6u3xDeIN?JkM1z4Zm%_R@T7X9i~Uf3NNuE@T}@TmglZKTQ2yqdDhw>TnSZ$=@IEOr&>BEiC%> z?)}?R_Uh}`yH^h}*GiNd*ae>`dN#ke*!<~n^GH1z!b-FRcdLnQ{uqNQYU^N~>6vmEE_(0^@=OZb#bDCZsn=)LqSRC>L< zJx`ybdDR!%C8fJ24=q6Hyoh$Y^e6TLv(f}?1Ap5VLE@i9U@8?Txqusf8pflEDDNMG z%6dIAf+#ARJoXUYR7%&@N*;H;2Fuv<8lBD!J~ZL)+R94ws`CSizEiuQ$KtIiJ~aYI zVgjUWqjK_`TQd&1D22dEr}5}C466P6obpg57yljvnl0~&@iV@T_9N)YbiLU_3?)Mh z)I?@!ipioJQ`ATz)i<@qwP(4Z&X_KK1CH3@jMU|VE#FBQv_^_Pf)e#j3Lz_(=Kf=0 zb0GhO10ZqCcrpgD{nV?K;mffQA}7CU`F@1RgUtoTJZ4|-XW!*I-3Rov2DGM{YTWGw z)^nX-MIC)}lRP(?>Q%q4VjVSs+gULv5cKP{En8-@Vox&9F+9zq$F{>Q#ag#w*pfBLH_Q##tHUL@h!MZ6^Fu3zA+gzJtjuOdGj zcXuJ<`1N#ccbM_bzqF5`C4DKRnjImmV{erQO$07qcgj_jcuK80o7=sF+BS>E`Ci&)t(| zCa7*>3|+}P?WtRBrsX=o3S=8+0C`DQh+)-qsUg|ikttggBo&<lr-SzDJ-5JBhBSH8YmCzE2D2ky(?m-qKF-X=y-oikadwa+r@PPao^Ut02Ph+I*^zja<}@RLf#~*uiy0wNF-wt#==#&7J_R2?lJCGea%mO z&`!X+&K$I#vP*8-mh^Jt9TffLDq=vwlGXh{F0I{(Mu>!3Ex@w7D7Hvs+^W3uI^6?n zTE3eQB14u79*gjXo@Ch$KZcs*iySuorWi-$WB0fbS)qKuR)gbyc@sEGFJ(GL zS>>g0BfXOzbL%^`(6`hsKyIZo>uPvxh8HD1d>iy`6vpy64jmZfSbyNef++jdu%t!s zrh*h4b10WfF6dS5xc{U6W44Tf7>Oi%x?3#+5^k3Lo~{c1j>T6YZ7RqIrP^qX>ie^} zkA(#s?WjgMx(8ZUmDeF?47W`eP8&t21q9-Fi`5q<1R#B4=<~w3-&j}N?hQ-j&^b(R zfQcb9)P`h2AQWM^YCREVSfuK`Q+U{q=MHLh&a!)9(R*?EV*K!%d7Xo^^3pjGaPhiUHqNo=Ae9{K&hUp%e(77SjoamJ(1- z$;jFPI5;ixD}!rZ<#+_bq!`#wlWG|FnOb!|isR8aJjNGV_q=jV&hJ!NSeBv-oDLfc z79#GQR#;X=s&iKjy~tP!Gzm9M`Bm=AE-wonYWWB#<3XI$g9shNzlcDi z8U_oF))4TL$5U=a+~&6}P4e)mRrml>yasOa78WvmG$^mCcXSd zt0Tn47w#5)9Mm0*@q9XEhxssA##=0sCCj0Mt|8ckp=mHK&s|pLo*`>lP!M3+%dA-3 z*{%AmfZ&$J1GDBAw#?P)EX?+ds^mMb@bx zE`e4mC%?h~@d(NEPH!IFZ5NS?@(&$&hB=Yy*is8n~iX>2!dqTodFqJ~+4ba(y&`J9fF7 zCRyNZ&EBEQ+_yEwH}9@V3l$%>E_u3hra`xV?CoC3|Dw2<&tk`qj@|l`B;i$0_whU;ZuTEa? zowgCZD+4|Zc=K;E{=zo3s>o9oc6I{TjzgE*J^JnOfk=XO_qD}CC3IWDWN*deW5bn{p36%$ORrph1JQk3p>$LWshCX>-4&Zqu0_+ z)b`&ITJ7T5>OeCqRx=B6qU|{Nnx9!~Up;)-;ivktM2jY{YV6Zjtl(}}>_D4eXS2Mw zc2@x7;M1?(CNc(WZ9KWYp9sU~KEs~S#mV_S zq02V$K27L|#rZ4oi2e0x6`zfci1mG@s5#WMH?Wlx-muyAxy*3(<9#!mT| zzr3o!`?x=GE2qObx?9dHFu}Jhbbb9Jc|FI=I@1@*nW7*c{o-rh5rzf|7?@>W1yko(nr`@Kql|gBiKup;A}V zmVd|av9H01-8;{%v~jV9w`-JMGgIBa3Mp$#aPaa=U&7Fsk4$h}StHvPn zU?1Iz^I$P%K6b_uunrZ(pr7`~;@^v%9XG!4eX;4nbf+U2B;P*!;-b=zx!O;L6VbzR z0M5SH=AT|y(Jv^(W}m$&D_rvz5W>diY<`aG-dE1Z{4!Q6lAtB7PA^z&Z{a7}^t{k}@6-iK zw%;Afrft;ycoh<6X%Xd+aVn5J>5mGKe4PX-RCj?!0eS_hPCo9CtCo=IS!)azOV3km z*#F|RcmD6;Q7T8IDMN_?LGaxmoz`45QNOoQ5}p(K%)>%*BzK}wbBof&Q| zjN=F~Zn(W@dU()e$S@IAkQx)(Q>K-+wlm3#(MqpZ_PiBgb^JB4ECL~N<=5Ji=&a9w z)v3!4TK~Q5s5fM((3f5q>-6(s6vy*u*L<>}kvkWLzHSbc-U+r$esRY+q}rkc3@?{M z+!c>O@BT)~hOYhoA$j3&JI(%tpBm3Ew|^abJwQG%b60po#AL2u9o6%>;b_xi@6K8334J<5)DmuLcekEhSn{F zJm1~%?-jWnx;A#AD?t9yhm72Nqv1Sv3CA|El20B*SE$Br^sg>dHApPoI&Aw`HBauY zI-9$?$E|oLO03Pc8GCp0b+a-|fOek#gw!2Ef?`p?yFBbH-t-UNaE^;fkscH9Bv1eJ zbY_O|#muk5ppH8DgtJLAvYM8$BWOprYI+xjYqG$08xBlb8=7}~-@u#(mj4-hSFn`r z#~yZ(XegemKlBItm{C>f@^4^|eCN^Fg>7B9Bc*uks6A83rTf8B<^$2;Hpb|mGY_Y0 zucw0&vFjS2v68QHQyD9*UY%Me6xSd#?Kx$FdKdFUz!E}dSpPZnMnUNH6>!B~$0mNo z2X2Lg%qq{zW}z3x$|jw35Wy$ti(r1q~r&^h+Be-u%OP3czzBc3xM)atMz zhCXemaMjrW3Bym>gVOj|<#GSbM9;%c-eWo8NkX_r*j{L`1fIv;=FK#Xe+6sZC&GP< zM3Zj;JYRpfH~^}e9z&0U?m!dI6q958R!x$SL(qTL7^K7)k*l{51nG#VSnoY1t8 zFt_MPW+4#BvfLl?GN&*-=dG?lSZK(>t4Qe(=~(2AH1nt@Ez#?8a5MhU4=pkJzzK9v zQ!2!c39%_72nc*H9bga43q5}z%CuA1FNKc6+}vt;PHbkH91lU=PH!LS8_XY8YY#NV zTs0#wl&CevSv>T1L=Y_Q1F`DoeAYm7Fd@3We;CZ?&x^q2Ig-KStqZZ%lPo*Sg}MUk z%BjNgCU>cMy7r@+ET{law_S}OFO5W$Rc$pYSYd&IsRA!`(TyhbFUi@VO4;hi4j~gq z-~MapW(k33hoArYY232pw?w-$8VY8CNy35Vte##;xfSNaWPC0+4$bs^wv8*rN>vA^}N%yfBQEqDr9rqWPv9qX!Xm+2-!!Ce}t;5(BahCw)L^Z zJ2_AyOgs!J(YEsIXP+@RKbtn1aYpGz4Qa)IH!A`J;;39F_td#}@kg(3YghH`V`1}O z%f-_nUxS4iYXNwsYVg?_*plrU2o$D)99mU9)p+*tJWfUSR`kf5A1Zo0FV802&Y6ff zw(Zq%iM6eYK?jv4i_(%SQp3|5$}z#7H3#PoIN(^GUSk}zRok(eaiY*&v1Br%;7^{+ zhi^+(m`hnTX}Mgtx6%_ed0R8CwZu_8~WZw>BSx@Cx1(sh?=>kBezeLKxZz>!# z9i-ARHmQi;dZ);uc-5Jf9jZtQZ|`I$Q}sAsHu)}dj}CbI5(OLj+26Iw??L{t>~Jx4 zYYaRVflUNhW=pQ!;u19IANb6tkT=4J5Wv@OAYZx&0{woDg3veEgl-JPn$JSh54Edb zu8_i|ihvwPXrN1P6*_`bc*u||hXPD5fRlKucnIlpbwr#uDeHkZzzzW?r;zDj=( zk_>25h>ts*DL)hoT{V5LMGYDaiiQn&?c_hhd(GATHO;&0!wXYh5lIF-lFbu9Z%fe& z>1m~g0CfqJ4275#u_A&;fUojs73~yG`=| diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/res/raw/ic_launcher.png b/packages/image_picker/image_picker_android/example/android/app/src/main/res/raw/ic_launcher.png new file mode 100755 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@U Date: Fri, 3 Feb 2023 13:27:25 +0100 Subject: [PATCH 5/5] [image_picker_android] Fix analysis issues --- .../image_picker_android/example/lib/main.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/image_picker/image_picker_android/example/lib/main.dart b/packages/image_picker/image_picker_android/example/lib/main.dart index c7bb2c627649..34f9114332f5 100755 --- a/packages/image_picker/image_picker_android/example/lib/main.dart +++ b/packages/image_picker/image_picker_android/example/lib/main.dart @@ -97,11 +97,11 @@ class _MyHomePageState extends State { if (isVideo) { final XFile? file = await _picker.getVideo( source: source, maxDuration: const Duration(seconds: 10)); - if (file != null) { + if (file != null && context.mounted) { _showPickedSnackBar(context, [file]); } await _playVideo(file); - } else if (isMultiImage) { + } else if (isMultiImage && context.mounted) { await _displayPickImageDialog(context, (double? maxWidth, double? maxHeight, int? quality) async { try { @@ -110,7 +110,7 @@ class _MyHomePageState extends State { maxHeight: maxHeight, imageQuality: quality, ); - if (pickedFileList != null) { + if (pickedFileList != null && context.mounted) { _showPickedSnackBar(context, pickedFileList); } setState(() => _imageFileList = pickedFileList); @@ -128,7 +128,7 @@ class _MyHomePageState extends State { maxHeight: maxHeight, imageQuality: quality, ); - if (pickedFile != null) { + if (pickedFile != null && context.mounted) { _showPickedSnackBar(context, [pickedFile]); } setState(() => _setImageFileListFromFile(pickedFile));