skip to Main Content

I need to use camera in two screens when one screen push another screen.

In that case the camera shows black screen when I go back from second widget:

import 'dart:async';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';

List<CameraDescription> cameras = [];
late CameraDescription firstCamera;

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  cameras = await availableCameras();
  firstCamera = cameras.first;

  runApp(
    MaterialApp(
      theme: ThemeData.dark(),
      home: TakePictureScreen(
        camera: firstCamera,
      ),
    ),
  );
}

// A screen that allows users to take a picture using a given camera.
class TakePictureScreen extends StatefulWidget {
  const TakePictureScreen({
    super.key,
    required this.camera,
  });

  final CameraDescription camera;

  @override
  TakePictureScreenState createState() => TakePictureScreenState();
}

class TakePictureScreenState extends State<TakePictureScreen> {
  late CameraController? _controller;

  @override
  void initState() {
    super.initState();
    _controller = CameraController(
      widget.camera,
      ResolutionPreset.medium,
      enableAudio: false,
    );
    _controller?.initialize().then((value) => setState(() {}));
  }

  @override
  void dispose() async {
    super.dispose();
    await _controller?.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Take a picture')),
      body: _controller != null && _controller!.value.isInitialized
          ? CameraPreview(_controller!)
          : const Center(child: CircularProgressIndicator()),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          try {
            final navigator = Navigator.of(context);
            // final oldController = _controller;
            // setState(() => _controller = null);
            // await oldController?.dispose();
            // var cameras = await availableCameras();
            // var firstCamera = cameras.first;
            await navigator.push(
              MaterialPageRoute(
                builder: (context) => TakePictureScreen(
                  camera: firstCamera,
                ),
              ),
            );
            // cameras = await availableCameras();
            // firstCamera = cameras.first;
            // _controller = CameraController(
            //   widget.camera,
            //   ResolutionPreset.medium,
            //   enableAudio: false,
            // );
            await _controller?.initialize();
            setState(() {});
          } catch (e) {
            print(e);
          }
        },
        child: const Icon(Icons.camera_alt),
      ),
    );
  }
}

I tried everything (see commented out code) but didn’t succeed.

The only way is to pass controller as a parameter, but I can not do it in my project.

There is no such problem on android.

2

Answers


  1. Try to move the camera initialization into a separate _initializeCamera() function to make it easier to call

    Properly dispose of the camera controller in the dispose() method of the _TakePictureScreenState class.

    Re-initialize the camera controller in the onPressed callback of the floating action button after returning from the second screen.

    Login or Signup to reply.
    1. Add the following to ios/Runner/Info.plist:
    <key>NSCameraUsageDescription</key>
    <string>your usage description here</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>your usage description here</string>
    
    1. Create controller and initialize it.
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
    
      final cameras = await availableCameras();
    
      final controller = CameraController(
        cameras[0],
        ResolutionPreset.ultraHigh,
        enableAudio: false,
      );
    
      await controller.initialize();
    
      runApp(
        CameraProvider(
          controller: controller,
          child: const MaterialApp(home: FirstScreen()),
        ),
      );
    }
    
    1. Use parent widget to manage controller lifecycle and access:
    class CameraProvider extends InheritedWidget {
      const CameraProvider({
        super.key,
        required this.controller,
        required super.child,
      });
    
      final CameraController controller;
    
      static CameraProvider of(BuildContext context) {
        final cameraProvider =
            context.dependOnInheritedWidgetOfExactType<CameraProvider>();
    
        assert(cameraProvider != null, "$CameraProvider not found in context.");
    
        return cameraProvider!;
      }
    
      /// Call when finished using camera (i.e. in stateful widget dispose method).
      void dispose() {
        controller.dispose();
      }
    
      @override
      bool updateShouldNotify(CameraProvider oldWidget) {
        return controller != oldWidget.controller;
      }
    }
    
    1. Use context to access shared controller in different routes/screens:
    class FirstScreen extends StatelessWidget {
      const FirstScreen({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: SafeArea(
            child: Stack(
              children: [
                /// Context access through InheritedWidget.
                CameraProvider.of(context).controller.buildPreview(),
                Align(
                  alignment: Alignment.topRight,
                  child: FloatingActionButton(
                    onPressed: () => Navigator.push(
                      context,
                      MaterialPageRoute(builder: (context) => const SecondScreen()),
                    ),
                    heroTag: null,
                    child: const Icon(Icons.arrow_right),
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    class SecondScreen extends StatelessWidget {
      const SecondScreen({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: SafeArea(
            child: Stack(
              children: [
                /// Context access through InheritedWidget.
                CameraProvider.of(context).controller.buildPreview(),
                Align(
                  alignment: Alignment.topRight,
                  child: FloatingActionButton(
                    onPressed: () => Navigator.pop(context),
                    heroTag: null,
                    child: const Icon(Icons.arrow_left),
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search