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.
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
}