flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
166.19k stars 27.49k forks source link

Image imported with `ImagePicker().pickImage(requestFullMetadata: false)` loses EXIF orientation on some images (iOS) #120356

Closed mazzonem closed 1 year ago

mazzonem commented 1 year ago

Steps to Reproduce

  1. Use the image_picker library
  2. Use the pickImage function with the requestFullMetadata parameter set to false
  3. on iOS, this flag allows you to pick an image without asking for Photo Library Usage permission.
  4. The Exif orientation is not part of the image anymore

Expected results: The loaded image contains the EXIF orientation data Actual results: The loaded image does not contain the EXIF orientation data

This is a regression since moving to Flutter 3.7.x, Is still working fine with flutter 3.3.x. Interestingly, it seems to be a Fllutter issue rather than an image_picker library issue, image_picker didn't change version.

Code sample ```dart ```
Logs ``` ``` ``` ``` ``` ```
exaby73 commented 1 year ago

Hey @mazzonem. Could you provide a complete and minimal, reproducible example that reproduces this issue? Also, how are you verifying the EXIF data on the picked image?

mazzonem commented 1 year ago

@exaby73 See below the matrix showing the issue and a link to the example project.

The regression has been introduced in image_picker_ios: 0.8.6+7

The library is not pulling with the correct orientation when requestFullMetadata is false.

Github project: https://github.com/mazzonem/request_full_metadata_regression_demo

Matrix Showing the issue: 0.8.6+6 0.8.6+7
requestFullMetadata: false simulator_screenshot_7472CA0A-BFC9-4F53-A7E7-9052B3470C45 simulator_screenshot_89EA324D-8511-4114-B4E5-5E6DC18A7C28
requestFullMetadata: true simulator_screenshot_CBDEFB1B-3FB1-414F-B1C1-56BD3EF48F5B simulator_screenshot_C64723FD-DE97-4AE1-892A-FB035D8760B0
exaby73 commented 1 year ago

So is this working as expected when requestFullMetadata is set to true which is the default?

mazzonem commented 1 year ago

So is this working as expected when requestFullMetadata is set to true which is the default?

Indeed, but not anymore when set to false.

exaby73 commented 1 year ago

Hey. I cannot reproduce this with the images I have. Can you share a specific image which you can reproduce this with?

mazzonem commented 1 year ago

@exaby73 the test image needs to be a portrait photo taken with an iPhone. Github does not support ".HEIC" file so here is a download link: https://we.tl/t-0xQBHYneZ0

You can:

exaby73 commented 1 year ago

Triage report

This is reproducible with 3.3.10 as well. Doesn't look like a regression.

Code Sample (Same as OP) ```dart import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( // This is the theme of your application. // // Try running your application with "flutter run". You'll see the // application has a blue toolbar. Then, without quitting the app, try // changing the primarySwatch below to Colors.green and then invoke // "hot reload" (press "r" in the console where you ran "flutter run", // or simply save your changes to "hot reload" in a Flutter IDE). // Notice that the counter didn't reset back to zero; the application // is not restarted. primarySwatch: Colors.blue, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); // This widget is the home page of your application. It is stateful, meaning // that it has a State object (defined below) that contains fields that affect // how it looks. // This class is the configuration for the state. It holds the values (in this // case the title) provided by the parent (in this case the App widget) and // used by the build method of the State. Fields in a Widget subclass are // always marked "final". final String title; @override State createState() => _MyHomePageState(); } class _MyHomePageState extends State { Uint8List? imageBytes; String? path; @override Widget build(BuildContext context) { // This method is rerun every time setState is called, for instance as done // by the _incrementCounter method above. // // The Flutter framework has been optimized to make rerunning build methods // fast, so that you can just rebuild anything that needs updating rather // than having to individually change instances of widgets. return Scaffold( appBar: AppBar( // Here we take the value from the MyHomePage object that was created by // the App.build method, and use it to set our appbar title. title: Text(widget.title), ), body: Center( // Center is a layout widget. It takes a single child and positions it // in the middle of the parent. child: Column( // Column is also a layout widget. It takes a list of children and // arranges them vertically. By default, it sizes itself to fit its // children horizontally, and tries to be as tall as its parent. // // Invoke "debug painting" (press "p" in the console, choose the // "Toggle Debug Paint" action from the Flutter Inspector in Android // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) // to see the wireframe for each widget. // // Column has various properties to control how it sizes itself and // how it positions its children. Here we use mainAxisAlignment to // center the children vertically; the main axis here is the vertical // axis because Columns are vertical (the cross axis would be // horizontal). mainAxisAlignment: MainAxisAlignment.center, children: [ if (imageBytes != null) Image.memory( imageBytes!, height: 250, fit: BoxFit.cover, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () async { final ImagePicker picker = ImagePicker(); final XFile? file = await picker.pickImage( source: ImageSource.gallery, maxHeight: 1024, maxWidth: 1024, requestFullMetadata: false, ); if (file != null) { final imageBytesResult = await file.readAsBytes(); setState(() { imageBytes = imageBytesResult; }); } }, tooltip: 'Increment', child: const Icon(Icons.add), ), // This trailing comma makes auto-formatting nicer for build methods. ); } } ```

Image used compressed to ZIP

flutter doctor -v (Master) ``` [!] Flutter (Channel master, 3.8.0-12.0.pre.68, on macOS 13.2 22D49 darwin-arm64, locale en-US) • Flutter version 3.8.0-12.0.pre.68 on channel master at /Users/nabeelparkar/fvm/versions/master ! Warning: `dart` on your path resolves to /opt/homebrew/Cellar/dart/2.19.1/libexec/bin/dart, which is not inside your current Flutter SDK checkout at /Users/nabeelparkar/fvm/versions/master. Consider adding /Users/nabeelparkar/fvm/versions/master/bin to the front of your path. • Upstream repository https://github.com/flutter/flutter.git • Framework revision 4b8ad1b007 (86 minutes ago), 2023-02-14 22:29:23 -0800 • Engine revision d860892528 • Dart version 3.0.0 (build 3.0.0-233.0.dev) • DevTools version 2.21.1 • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades. [✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0) • Android SDK at /Users/nabeelparkar/Library/Android/sdk/ • Platform android-33, build-tools 33.0.0 • ANDROID_SDK_ROOT = /Users/nabeelparkar/Library/Android/sdk/ • Java binary at: /Users/nabeelparkar/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/221.6008.13.2211.9477386/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 11.0.15+0-b2043.56-8887301) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 14.2) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 14C18 • CocoaPods version 1.11.3 [✓] Chrome - develop for the web • CHROME_EXECUTABLE = /Applications/Brave Browser.app/Contents/MacOS/Brave Browser [✓] Android Studio (version 2021.3) • Android Studio at /Users/nabeelparkar/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/213.7172.25.2113.9123335/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866) [✓] Android Studio (version 2022.1) • Android Studio at /Users/nabeelparkar/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/221.6008.13.2211.9477386/Android Studio.app/Contents • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 11.0.15+0-b2043.56-8887301) [✓] VS Code (version 1.75.1) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.58.0 [✓] Connected device (3 available) • Nabeel’s iPhone (mobile) • 00008101-000E48283A20001E • ios • iOS 16.2 20C65 • macOS (desktop) • macos • darwin-arm64 • macOS 13.2 22D49 darwin-arm64 • Chrome (web) • chrome • web-javascript • Brave Browser 110.1.48.158 [✓] Network resources • All expected network resources are available. ! Doctor found issues in 1 category. ```
jmagman commented 1 year ago

cc @vashworth re: https://github.com/flutter/plugins/pull/7084

github-actions[bot] commented 1 year ago

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.