Flutter FutureBuilder camera has been intialized already

88 views Asked by At

I'm new to Flutter and I want to make a simple camera screen using FutureBuilder. This is my code

import 'dart:developer';

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

import 'DisplayImagePage.dart';

class CameraScreen extends StatefulWidget {

  @override
  _CameraScreenState createState() => _CameraScreenState();

}

class _CameraScreenState extends State<CameraScreen> {
  late CameraController _controller;
  late final CameraDescription camera;


  @override
  void initState() {
    super.initState();
  }

  Future<void> prepareCamera() async
   {
    _getCamera().then((CameraDescription camera)
    {
      _controller = CameraController(camera, ResolutionPreset.medium);
      _controller.initialize().then((_) {
        if (!mounted) {
          return;
        }
        setState(() {});
      });
    }
    );
  }

  Future<CameraDescription> _getCamera() async
  {

    final cameras = await availableCameras();
    if (cameras.isEmpty) {
      // No cameras available
      log("Camera is empty!");
    }
    final firstCamera = cameras.first;
    camera = firstCamera;
    return firstCamera;
  }

  Future<String> _takeSelfie(BuildContext context) async {

    final XFile image = await _controller.takePicture();
    var takenImagePath = image.path;

    return takenImagePath;

    }

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

  @override
  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(
        title: Text('Take a Selfie'),
      ),
      body: FutureBuilder(
        future: prepareCamera(),
        builder: (ctx, snapshot)
        {
          if(snapshot.hasData)
          {
            log(snapshot.toString());
            return AspectRatio(
              aspectRatio: _controller.value.aspectRatio,
              child: CameraPreview(_controller),
            );
          }
          else if (snapshot.hasError)
          {
            return Center(
              child: Text(
                '${snapshot.error} occured',
                style: TextStyle(fontSize: 18),
              ),
            );
          }
          return Center(
            child: CircularProgressIndicator(),
          );
        }
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          try {
            var path = await _takeSelfie(context);
            Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (context) => DisplayImagePage(imageUrl: path),
                ));
          } catch (e) {
            print(e);
          }
        },
        child: Icon(Icons.camera),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
    );
  }
}

After I give permission to the camera acces, I see this error on the console: Error: LateInitializationError: Field 'camera' has already been initialized.

_getCamera() is only called once why it tries to assign camera again?

I have tried to wrap _getCamera and PrepareCamera functions together. This did not work.

1

There are 1 answers

4
Dhafin Rayhan On BEST ANSWER

The build method is called everytime the state of your StatefulWidget is updated, so the FutureBuilder will call prepareCamera on every build. To prevent this, I suggest you to make camera nullable instead of using late initilization, and make the logic inside _getCamera like this:

class _CameraScreenState extends State<CameraScreen> {
  late CameraController _controller;
  CameraDescription? camera;

  // ...
}
Future<CameraDescription> _getCamera() async
{
  if (camera == null) {
    final cameras = await availableCameras();
    if (cameras.isEmpty) {
      // No cameras available
      log("Camera is empty!");
    }
    final firstCamera = cameras.first;
    camera = firstCamera;
    return firstCamera;
  }
  
  return camera; // add this too
}