-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Open
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work liste: device-specificOnly manifests on certain devicesOnly manifests on certain devicesp: cameraThe camera pluginThe camera pluginpackageflutter/packages repository. See also p: labels.flutter/packages repository. See also p: labels.platform-iosiOS applications specificallyiOS applications specificallyteam-iosOwned by iOS platform teamOwned by iOS platform teamtriaged-iosTriaged by iOS platform teamTriaged by iOS platform team
Description
Steps to Reproduce
How to reproduce the problem :
Change camera to new camera many times ( calling function onSetFlashModeButtonPressed in the code)
Plugin version: camera: ^0.8.1
Flutter version: 2.0.5
Console Output
======== Exception caught by widgets library =======================================================
The following CameraException was thrown building CameraPreview(dirty):
CameraException(Disposed CameraController, buildPreview() was called on a disposed CameraController.)
The relevant error-causing widget was:
CameraPreview file:///Users/mac/Documents/projects/mobile-app_backup/lib/app/presentation/page/camera/app_camera.dart:181:26
When the exception was thrown, this was the stack:
#0 CameraController._throwIfNotInitialized (package:camera/src/camera_controller.dart:775:7)
#1 CameraController.buildPreview (package:camera/src/camera_controller.dart:529:5)
#2 CameraPreview.build (package:camera/src/camera_preview.dart:31:53)
#3 StatelessElement.build (package:flutter/src/widgets/framework.dart:4569:28)
#4 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4495:15)
...
====================================================================================================
* thread #28, queue = 'io.flutter.camera.dispatchqueue', stop reason = EXC_BAD_ACCESS (code=2, address=0x5cb218d6a0)
frame #0: 0x00000001bde5f0f8 libobjc.A.dylib`objc_msgSend + 24
libobjc.A.dylib`objc_msgSend:
-> 0x1bde5f0f8 <+24>: ldr x11, [x16, #0x10]
0x1bde5f0fc <+28>: tbnz w11, #0x0, 0x1bde5f158 ; <+120>
0x1bde5f100 <+32>: and x10, x11, #0xffffffffffff
0x1bde5f104 <+36>: eor x12, x1, x1, lsr #7
Target 0: (Runner) stopped.
Lost connection to device.
Code repo
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:video_player/video_player.dart';
import 'package:yool/app/presentation/style/app_colors.dart';
import 'package:yool/app/presentation/style/app_text_style.dart';
import 'package:yool/app/presentation/style/icons_yool_icons.dart';
import 'dart:async';
class AppCamera extends StatefulWidget {
const AppCamera({
Key? key,
}) : super(key: key);
@override
_AppCameraState createState() => _AppCameraState();
}
class _AppCameraState extends State<AppCamera>
with WidgetsBindingObserver, TickerProviderStateMixin {
CameraController? controller;
List<CameraDescription> cameras = <CameraDescription>[];
XFile? imageFile;
XFile? videoFile;
VideoPlayerController? videoController;
VoidCallback? videoPlayerListener;
bool enableAudio = true;
double _minAvailableExposureOffset = 0.0;
double _maxAvailableExposureOffset = 0.0;
double _currentExposureOffset = 0.0;
double _minAvailableZoom = 1.0;
double _maxAvailableZoom = 1.0;
double _currentScale = 1.0;
double _baseScale = 1.0;
// Counting pointers (number of user fingers on screen)
int _pointers = 0;
ValueNotifier<FlashMode> _flashNotifier =
ValueNotifier<FlashMode>(FlashMode.off);
@override
void initState() {
super.initState();
print("initState");
WidgetsBinding.instance?.addObserver(this);
_getAvailableCameras();
}
Future<void> _getAvailableCameras() async {
cameras = await availableCameras();
print(cameras.length);
if (cameras.isNotEmpty) {
await initCamera(cameras.first);
}
}
Future<void> initCamera(CameraDescription cameraDescription) async {
if (controller != null) controller!.dispose();
controller = CameraController(
// Get a specific camera from the list of available cameras.
cameraDescription,
// Define the resolution to use.
ResolutionPreset.max,
imageFormatGroup: ImageFormatGroup.jpeg);
controller!.initialize().then((value) => setState(() {
_flashNotifier.value = controller!.value.flashMode;
}));
}
@override
void dispose() {
WidgetsBinding.instance?.removeObserver(this);
// controller?.dispose();
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
final CameraController? cameraController = controller;
// App state changed before we got the chance to initialize.
if (cameraController == null || !cameraController.value.isInitialized) {
return;
}
if (state == AppLifecycleState.inactive) {
cameraController.dispose();
} else if (state == AppLifecycleState.resumed) {
onNewCameraSelected(cameraController.description);
}
}
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
void onNewCameraSelected(CameraDescription cameraDescription) async {
if (controller != null) {
await controller!.dispose();
}
final CameraController cameraController = CameraController(
cameraDescription,
ResolutionPreset.max,
imageFormatGroup: ImageFormatGroup.jpeg,
);
controller = cameraController;
// If the controller is updated then update the UI.
cameraController.addListener(() {
if (mounted) setState(() {});
if (cameraController.value.hasError) {
showInSnackBar(
'Camera error ${cameraController.value.errorDescription}');
}
});
try {
await cameraController.initialize();
_flashNotifier.value = cameraController.value.flashMode;
await Future.wait([
cameraController
.getMinExposureOffset()
.then((value) => _minAvailableExposureOffset = value),
cameraController
.getMaxExposureOffset()
.then((value) => _maxAvailableExposureOffset = value),
cameraController
.getMaxZoomLevel()
.then((value) => _maxAvailableZoom = value),
cameraController
.getMinZoomLevel()
.then((value) => _minAvailableZoom = value),
]);
} on CameraException catch (e) {
_showCameraException(e);
}
if (mounted) {
setState(() {});
}
}
void showInSnackBar(String message) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(message)));
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return Scaffold(
key: _scaffoldKey,
body: Stack(
children: <Widget>[
AspectRatio(
aspectRatio: size.aspectRatio,
child: Builder(
builder: (context) {
if (cameras.isEmpty)
return Container(
color: AppColors.navyBlue,
child: Center(
child: Text(
"camera_not_found",
style: AppTextStyle.bold16
.copyWith(color: AppColors.white),
),
),
);
return Listener(
onPointerDown: (_) => _pointers++,
onPointerUp: (_) => _pointers--,
child: CameraPreview(
controller!,
child: LayoutBuilder(builder:
(BuildContext context, BoxConstraints constraints) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onScaleStart: _handleScaleStart,
onScaleUpdate: _handleScaleUpdate,
onTapDown: (details) =>
onViewFinderTap(details, constraints),
);
}),
),
);
},
),
),
SafeArea(
child: IconButton(
icon: Icon(
IconsYool.left,
color: AppColors.white,
),
onPressed: () => Navigator.pop(context)),
),
Align(
alignment: Alignment.bottomCenter,
child: SafeArea(
top: false,
child: Padding(
padding: const EdgeInsets.only(bottom: 38),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
BottomButton(
icon: ValueListenableBuilder<FlashMode>(
valueListenable: _flashNotifier,
builder: (context, value, _) {
if (value == FlashMode.torch||value==FlashMode.always)
return Icon(
IconsYool.sr_brightness_up,
color: AppColors.yellow,
);
else
return Icon(
IconsYool.sr_brightness_up,
);
}),
onPressed: () {
if (controller == null ||
!controller!.value.isInitialized) {
return;
}
switch (_flashNotifier.value) {
case FlashMode.off:
if (controller!.description.lensDirection ==
CameraLensDirection.front)
setFlashMode(FlashMode.always);
else
setFlashMode(FlashMode.torch);
return;
default:
setFlashMode(FlashMode.off);
return;
}
},
),
BottomButton(
height: 72,
width: 72,
),
BottomButton(
onPressed: () async {
if (controller == null || cameras.length < 2) return;
if (controller?.description.lensDirection ==
cameras.first.lensDirection)
onNewCameraSelected(cameras[1]);
else
onNewCameraSelected(cameras.first);
},
icon: Icon(
IconsYool.sr_refrest,
),
),
],
),
),
),
)
],
),
);
}
void onSetFlashModeButtonPressed(FlashMode mode) {
setFlashMode(mode).then((_) {
if (mounted) setState(() {});
showInSnackBar('Flash mode set to ${mode.toString().split('.').last}');
});
}
Future<void> setFlashMode(FlashMode mode) async {
if (controller == null) {
return;
}
try {
await controller!.setFlashMode(mode);
_flashNotifier.value = mode;
} on CameraException catch (e) {
_showCameraException(e);
rethrow;
}
}
void _showCameraException(CameraException e) {
print([e.code, e.description]);
showInSnackBar('Error: ${e.code}\n${e.description}');
}
void _handleScaleStart(ScaleStartDetails details) {
_baseScale = _currentScale;
}
Future<void> _handleScaleUpdate(ScaleUpdateDetails details) async {
// When there are not exactly two fingers on screen don't scale
if (controller == null || _pointers != 2) {
return;
}
_currentScale = (_baseScale * details.scale)
.clamp(_minAvailableZoom, _maxAvailableZoom);
await controller!.setZoomLevel(_currentScale);
}
void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) {
if (controller == null) {
return;
}
final CameraController cameraController = controller!;
final offset = Offset(
details.localPosition.dx / constraints.maxWidth,
details.localPosition.dy / constraints.maxHeight,
);
cameraController.setExposurePoint(offset);
cameraController.setFocusPoint(offset);
}
}
class BottomButton extends StatelessWidget {
final double? height;
final double? width;
final Widget? icon;
final VoidCallback? onPressed;
final VoidCallback? onLongPress;
const BottomButton({
Key? key,
this.height,
this.width,
this.icon,
this.onPressed,
this.onLongPress,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return InkWell(
onLongPress: onLongPress,
onTap: onPressed,
child: Container(
height: height ?? 56,
width: width ?? 56,
decoration:
BoxDecoration(color: AppColors.white, shape: BoxShape.circle),
child: icon),
);
}
}
VictorUvarov and kauemurakami
Metadata
Metadata
Assignees
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work liste: device-specificOnly manifests on certain devicesOnly manifests on certain devicesp: cameraThe camera pluginThe camera pluginpackageflutter/packages repository. See also p: labels.flutter/packages repository. See also p: labels.platform-iosiOS applications specificallyiOS applications specificallyteam-iosOwned by iOS platform teamOwned by iOS platform teamtriaged-iosTriaged by iOS platform teamTriaged by iOS platform team