-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Closed
flutter/engine
#45182Labels
P1High-priority issues at the top of the work listHigh-priority issues at the top of the work listc: crashStack traces logged to the consoleStack traces logged to the consolec: fatal crashCrashes that terminate the processCrashes that terminate the processc: regressionIt was better in the past than it is nowIt was better in the past than it is nowe: impellerImpeller rendering backend issues and features requestsImpeller rendering backend issues and features requestsengineflutter/engine related. See also e: labels.flutter/engine related. See also e: labels.found in release: 3.13Found to occur in 3.13Found to occur in 3.13found in release: 3.14Found to occur in 3.14Found to occur in 3.14has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onp: image_pickerThe Image Picker plugin.The Image Picker plugin.packageflutter/packages repository. See also p: labels.flutter/packages repository. See also p: labels.platform-iosiOS applications specificallyiOS applications specificallyr: fixedIssue is closed as already fixed in a newer versionIssue is closed as already fixed in a newer versionteam-engineOwned by Engine teamOwned by Engine teamtriaged-engineTriaged by Engine teamTriaged by Engine team
Description
Is there an existing issue for this?
- I have searched the existing issues
- I have read the guide to filing a bug
Steps to reproduce
With the new flutter version, the image picker crashes once the imagesource.gallery is used.
- Install Flutter 3.13.0
- Add Gallery permissions to info.plist
- Run Demo App on iOS (non Simulator device)
- Tap Gallery button
- Grant permission
- See app crash
Downgrading Flutter to 3.10.6 and running the same steps will open the gallery as expected.
Info.plist permissions that need to be added:
NSCameraUsageDescription
Text reasoning..
NSMicrophoneUsageDescription
Text reasoning..
NSPhotoLibraryUsageDescription
Text reasoning..
dependencies that need to be added:
video_player: 2.0.0
image_picker: ^1.0.2
Expected results
Gallery opens, Video can be picked.
Actual results
App crashes and is closed.
Code sample
Code sample - based on image picker example project, removed unnecessary parts
// 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.
// ignore_for_file: public_member_api_docs
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';
import 'package:video_player/video_player.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Image Picker Demo',
home: MyHomePage(title: 'Image Picker Example'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, this.title});
final String? title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<XFile>? _mediaFileList;
void _setImageFileListFromFile(XFile? value) {
_mediaFileList = value == null ? null : <XFile>[value];
}
dynamic _pickImageError;
bool isVideo = false;
VideoPlayerController? _controller;
VideoPlayerController? _toBeDisposed;
String? _retrieveDataError;
final ImagePicker _picker = ImagePicker();
final TextEditingController maxWidthController = TextEditingController();
final TextEditingController maxHeightController = TextEditingController();
final TextEditingController qualityController = TextEditingController();
Future<void> _playVideo(XFile? file) async {
if (file != null && mounted) {
await _disposeVideoController();
late VideoPlayerController controller;
if (kIsWeb) {
// TODO(gabrielokura): remove the ignore once the following line can migrate to
// use VideoPlayerController.networkUrl after the issue is resolved.
// https://github.com/flutter/flutter/issues/121927
// ignore: deprecated_member_use
controller = VideoPlayerController.network(file.path);
} else {
controller = VideoPlayerController.file(File(file.path));
}
_controller = controller;
// In web, most browsers won't honor a programmatic call to .play
// if the video has a sound track (and is not muted).
// Mute the video so it auto-plays in web!
// This is not needed if the call to .play is the result of user
// interaction (clicking on a "play" button, for example).
const double volume = kIsWeb ? 0.0 : 1.0;
await controller.setVolume(volume);
await controller.initialize();
await controller.setLooping(true);
await controller.play();
setState(() {});
}
}
Future<void> _onImageButtonPressed(
ImageSource source, {
required BuildContext context,
bool isMultiImage = false,
bool isMedia = false,
}) async {
if (_controller != null) {
await _controller!.setVolume(0.0);
}
if (context.mounted) {
if (isVideo) {
final XFile? file = await _picker.pickVideo(
source: source, maxDuration: const Duration(seconds: 10));
await _playVideo(file);
} else if (isMultiImage) {
await _displayPickImageDialog(context,
(double? maxWidth, double? maxHeight, int? quality) async {
try {
final List<XFile> pickedFileList = isMedia
? await _picker.pickMultipleMedia(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality,
)
: await _picker.pickMultiImage(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality,
);
setState(() {
_mediaFileList = pickedFileList;
});
} catch (e) {
setState(() {
_pickImageError = e;
});
}
});
} else if (isMedia) {
await _displayPickImageDialog(context,
(double? maxWidth, double? maxHeight, int? quality) async {
try {
final List<XFile> pickedFileList = <XFile>[];
final XFile? media = await _picker.pickMedia(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality,
);
if (media != null) {
pickedFileList.add(media);
setState(() {
_mediaFileList = pickedFileList;
});
}
} catch (e) {
setState(() {
_pickImageError = e;
});
}
});
} else {
await _displayPickImageDialog(context,
(double? maxWidth, double? maxHeight, int? quality) async {
try {
final XFile? pickedFile = await _picker.pickImage(
source: source,
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality,
);
setState(() {
_setImageFileListFromFile(pickedFile);
});
} catch (e) {
setState(() {
_pickImageError = e;
});
}
});
}
}
}
@override
void deactivate() {
if (_controller != null) {
_controller!.setVolume(0.0);
_controller!.pause();
}
super.deactivate();
}
@override
void dispose() {
_disposeVideoController();
maxWidthController.dispose();
maxHeightController.dispose();
qualityController.dispose();
super.dispose();
}
Future<void> _disposeVideoController() async {
if (_toBeDisposed != null) {
await _toBeDisposed!.dispose();
}
_toBeDisposed = _controller;
_controller = null;
}
Widget _previewVideo() {
final Text? retrieveError = _getRetrieveErrorWidget();
if (retrieveError != null) {
return retrieveError;
}
if (_controller == null) {
return const Text(
'You have not yet picked a video',
textAlign: TextAlign.center,
);
}
return Padding(
padding: const EdgeInsets.all(10.0),
child: AspectRatioVideo(_controller),
);
}
Widget _previewImages() {
final Text? retrieveError = _getRetrieveErrorWidget();
if (retrieveError != null) {
return retrieveError;
}
if (_mediaFileList != null) {
return Semantics(
label: 'image_picker_example_picked_images',
child: ListView.builder(
key: UniqueKey(),
itemBuilder: (BuildContext context, int index) {
final String? mime = lookupMimeType(_mediaFileList![index].path);
// Why network for web?
// See https://pub.dev/packages/image_picker_for_web#limitations-on-the-web-platform
return Semantics(
label: 'image_picker_example_picked_image',
child: kIsWeb
? Image.network(_mediaFileList![index].path)
: (mime == null || mime.startsWith('image/')
? Image.file(
File(_mediaFileList![index].path),
errorBuilder: (BuildContext context, Object error,
StackTrace? stackTrace) {
return const Center(
child:
Text('This image type is not supported'));
},
)
: _buildInlineVideoPlayer(index)),
);
},
itemCount: _mediaFileList!.length,
),
);
} else if (_pickImageError != null) {
return Text(
'Pick image error: $_pickImageError',
textAlign: TextAlign.center,
);
} else {
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
}
}
Widget _buildInlineVideoPlayer(int index) {
final VideoPlayerController controller =
VideoPlayerController.file(File(_mediaFileList![index].path));
const double volume = kIsWeb ? 0.0 : 1.0;
controller.setVolume(volume);
controller.initialize();
controller.setLooping(true);
controller.play();
return Center(child: AspectRatioVideo(controller));
}
Widget _handlePreview() {
if (isVideo) {
return _previewVideo();
} else {
return _previewImages();
}
}
Future<void> retrieveLostData() async {
final LostDataResponse response = await _picker.retrieveLostData();
if (response.isEmpty) {
return;
}
if (response.file != null) {
if (response.type == RetrieveType.video) {
isVideo = true;
await _playVideo(response.file);
} else {
isVideo = false;
setState(() {
if (response.files == null) {
_setImageFileListFromFile(response.file);
} else {
_mediaFileList = response.files;
}
});
}
} else {
_retrieveDataError = response.exception!.code;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title!),
),
body: Center(
child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android
? FutureBuilder<void>(
future: retrieveLostData(),
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
case ConnectionState.done:
return _handlePreview();
case ConnectionState.active:
if (snapshot.hasError) {
return Text(
'Pick image/video error: ${snapshot.error}}',
textAlign: TextAlign.center,
);
} else {
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
}
}
},
)
: _handlePreview(),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
backgroundColor: Colors.red,
onPressed: () {
isVideo = true;
_onImageButtonPressed(ImageSource.gallery, context: context);
},
heroTag: 'video0',
tooltip: 'Pick Video from gallery',
child: const Icon(Icons.video_library),
),
),
if (_picker.supportsImageSource(ImageSource.camera))
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
backgroundColor: Colors.red,
onPressed: () {
isVideo = true;
_onImageButtonPressed(ImageSource.camera, context: context);
},
heroTag: 'video1',
tooltip: 'Take a Video',
child: const Icon(Icons.videocam),
),
),
],
),
);
}
Text? _getRetrieveErrorWidget() {
if (_retrieveDataError != null) {
final Text result = Text(_retrieveDataError!);
_retrieveDataError = null;
return result;
}
return null;
}
Future<void> _displayPickImageDialog(
BuildContext context, OnPickImageCallback onPick) async {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Add optional parameters'),
content: Column(
children: <Widget>[
TextField(
controller: maxWidthController,
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
decoration: const InputDecoration(
hintText: 'Enter maxWidth if desired'),
),
TextField(
controller: maxHeightController,
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
decoration: const InputDecoration(
hintText: 'Enter maxHeight if desired'),
),
TextField(
controller: qualityController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
hintText: 'Enter quality if desired'),
),
],
),
actions: <Widget>[
TextButton(
child: const Text('CANCEL'),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: const Text('PICK'),
onPressed: () {
final double? width = maxWidthController.text.isNotEmpty
? double.parse(maxWidthController.text)
: null;
final double? height = maxHeightController.text.isNotEmpty
? double.parse(maxHeightController.text)
: null;
final int? quality = qualityController.text.isNotEmpty
? int.parse(qualityController.text)
: null;
onPick(width, height, quality);
Navigator.of(context).pop();
}),
],
);
});
}
}
typedef OnPickImageCallback = void Function(
double? maxWidth, double? maxHeight, int? quality);
class AspectRatioVideo extends StatefulWidget {
const AspectRatioVideo(this.controller, {super.key});
final VideoPlayerController? controller;
@override
AspectRatioVideoState createState() => AspectRatioVideoState();
}
class AspectRatioVideoState extends State<AspectRatioVideo> {
VideoPlayerController? get controller => widget.controller;
bool initialized = false;
void _onVideoControllerUpdate() {
if (!mounted) {
return;
}
if (initialized != controller!.value.isInitialized) {
initialized = controller!.value.isInitialized;
setState(() {});
}
}
@override
void initState() {
super.initState();
controller!.addListener(_onVideoControllerUpdate);
}
@override
void dispose() {
controller!.removeListener(_onVideoControllerUpdate);
super.dispose();
}
@override
Widget build(BuildContext context) {
if (initialized) {
return Center(
child: AspectRatio(
aspectRatio: controller!.value.aspectRatio,
child: VideoPlayer(controller!),
),
);
} else {
return Container();
}
}
}
Screenshots or Video
Screenshots / Video demonstration
[Upload media here]
Logs
Logs
`[LayoutConstraints] Unsupported layout off the main thread for UITextEffectsWindow with no associated or ancestor view controller
This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
Stack:(
0 CoreAutoLayout 0x00000001b5425dc0 11B7BCA7-7250-30FB-B4D8-A902FEE394D9 + 56768
1 CoreAutoLayout 0x00000001b541c994 11B7BCA7-7250-30FB-B4D8-A902FEE394D9 + 18836
2 CoreAutoLayout 0x00000001b541c8c4 11B7BCA7-7250-30FB-B4D8-A902FEE394D9 + 18628
3 CoreAutoLayout 0x00000001b541c650 11B7BCA7-7250-30FB-B4D8-A902FEE394D9 + 18000
4 UIKitCore 0x0000000199fbfc60 7D57A1D1-856F-338D-97DB-880C4EC8B02E + 23648
5 UIKitCore 0x0000000199fbe420 7D57A1D1-856F-338D-97DB-880C4EC8B02E + 17440
6 QuartzCore 0x0000000199478f30 8682CC56-125A-3565-8075-978CC718B93F + 40752
7 QuartzCore 0x000000019948c4ac 8682CC56-125A-3565-8075-978CC718B93F + 119980
8 QuartzCore 0x000000019949d8d8 8682CC56-125A-3565-8075-978CC718B93F + 190680
9 QuartzCore 0x00000001994cce80 8682CC56-125A-3565-8075-978CC718B93F + 384640
10 QuartzCore 0x00000001994b6df0 8682CC56-125A-3565-8075-978CC718B93F + 294384
11 CoreFoundation 0x0000000197fdb234 A900B459-0127-379E-9CBA-0EAB9C5D559F + 590388
12 CoreFoundation 0x0000000197f65410 A900B459-0127-379E-9CBA-0EAB9C5D559F + 107536
13 CoreFoundation 0x0000000197fc519c A900B459-0127-379E-9CBA-0EAB9C5D559F + 500124
14 CoreFoundation 0x0000000197fca3ec CFRunLoopRunSpecific + 612
15 Flutter 0x000000010510803c _ZN3fml17MessageLoopDarwin3RunEv + 88
16 Flutter 0x0000000105106be0 _ZNSt21_LIBCPP_ABI_NAMESPACE14__thread_proxyB6v15000INS_5tupleIJNS_10unique_ptrINS_15__thread_structENS_14default_deleteIS3_EEEEZN3fml6ThreadC1ERKNS_8functionIFvRKNS8_12ThreadConfigEEEESC_E3$_0EEEEEPvSJ_ + 208
17 libsystem_pthread.dylib 0x00000001f7d866b8 _pthread_start + 148
18 libsystem_pthread.dylib 0x00000001f7d85b88 thread_start + 8
)
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.'
*** First throw call stack:
(0x197f54cb4 0x190ff03d0 0x1b5425e68 0x1b541c994 0x1b541c8c4 0x1b541c650 0x199fbfc60 0x199fbe420 0x199478f30 0x19948c4ac 0x19949d8d8 0x1994cce80 0x1994b6df0 0x197fdb234 0x197f65410 0x197fc519c 0x197fca3ec 0x10510803c 0x105106be0 0x1f7d866b8 0x1f7d85b88)
libc++abi: terminating due to uncaught exception of type NSException
* thread #10, name = 'io.flutter.1.raster', stop reason = signal SIGABRT
frame #0: 0x00000001d6f11578 libsystem_kernel.dylib`__pthread_kill + 8
libsystem_kernel.dylib`:
-> 0x1d6f11578 <+8>: b.lo 0x1d6f11598 ; <+40>
0x1d6f1157c <+12>: pacibsp
0x1d6f11580 <+16>: stp x29, x30, [sp, #-0x10]!
0x1d6f11584 <+20>: mov x29, sp
Target 0: (Runner) stopped.
Lost connection to device.`Flutter Doctor output
Doctor output
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.13.0, on macOS 13.4.1 22F770820d darwin-arm64, locale en-DE)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 14.3.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2022.3)
[✓] VS Code (version 1.81.1)
[✓] Connected device (4 available)
[✓] Network resources
ebjorklund01, mgyarmathy, giorgiogross, AlexOrthopy, luis901101 and 10 more
Metadata
Metadata
Assignees
Labels
P1High-priority issues at the top of the work listHigh-priority issues at the top of the work listc: crashStack traces logged to the consoleStack traces logged to the consolec: fatal crashCrashes that terminate the processCrashes that terminate the processc: regressionIt was better in the past than it is nowIt was better in the past than it is nowe: impellerImpeller rendering backend issues and features requestsImpeller rendering backend issues and features requestsengineflutter/engine related. See also e: labels.flutter/engine related. See also e: labels.found in release: 3.13Found to occur in 3.13Found to occur in 3.13found in release: 3.14Found to occur in 3.14Found to occur in 3.14has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onp: image_pickerThe Image Picker plugin.The Image Picker plugin.packageflutter/packages repository. See also p: labels.flutter/packages repository. See also p: labels.platform-iosiOS applications specificallyiOS applications specificallyr: fixedIssue is closed as already fixed in a newer versionIssue is closed as already fixed in a newer versionteam-engineOwned by Engine teamOwned by Engine teamtriaged-engineTriaged by Engine teamTriaged by Engine team