Skip to content

[CAMERA] Lost connection  #81816

@II11II

Description

@II11II

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),
    );
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work liste: device-specificOnly manifests on certain devicesp: cameraThe camera pluginpackageflutter/packages repository. See also p: labels.platform-iosiOS applications specificallyteam-iosOwned by iOS platform teamtriaged-iosTriaged by iOS platform team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions