I want to build a screen for a flutter app which shows a camera preview. To achieve this I have to initialize the camera asynchronously. This leads to an exception because I access the camera before it is initialized. I work around that with using a try-catch block. But this strikes me as a dirty solution. Is there a better one? I am developing on windows for android. here is my full code for that screen. I marked the points of interest with line comments.
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
class CameraPreviewPage extends StatefulWidget {
const CameraPreviewPage({super.key});
@override
State<CameraPreviewPage> createState() => _CameraPreviewPageState();
}
class _CameraPreviewPageState extends State<CameraPreviewPage> {
late List<CameraDescription> _cameraDescriptions;
late CameraController _cameraController; //Here I declare the camera object which throws an "late not inizialized" exception
//this function inizializes the object but asynchronously
void initCamera() async {
_cameraDescriptions = await availableCameras();
_cameraController = CameraController(
_cameraDescriptions[0],
ResolutionPreset.high,
enableAudio: false,
);
await _cameraController.initialize().then((value) {
if (!mounted) {
return;
}
setState(() {});
}).catchError((e) {
debugPrint(e);
});
}
@override
void dispose() {
_cameraController.dispose();
super.dispose();
}
@override
void initState() {
initCamera();
super.initState();
}
//in this build function the Exception occurs.
// Wrapping the "CameraPreview(_cameraController)," line in a try catch gives me working but ugly(?) code
@override
Widget build(BuildContext context) {
try {
return Scaffold(
body: Stack(
children: [
CameraPreview(_cameraController),
],
),
);
} catch (e) {
return const SizedBox();
}
}
}
I am pretty new to asynchronous programming and to flutter/dart itself. If this question gets answered somewhere else plsease lead me there because I couldn’t find it on google/youtube.
I tried the FutureBuilder Widget but it did not work correctly, maybe I didn’t understand it and sadly I lost the code.
>Solution :
The problem is that initState itself is not an async method and cannot await your initCamera. So your initCamera is executing while your build method is already being called.
And then your _cameraController is not initialized yet, but used inside of your build method.
To fix this, you can conditionally show different widgets depending on a state bool, or use a FutureBuilder widget instead.
Your state class when using a bool:
class _CameraPreviewPageState extends State<CameraPreviewPage> {
late List<CameraDescription> _cameraDescriptions;
late CameraController _cameraController; //Here I declare the camera object which throws an "late not inizialized" exception
bool _initialized = false;
//this function inizializes the object but asynchronously
void initCamera() async {
_cameraDescriptions = await availableCameras();
_cameraController = CameraController(
_cameraDescriptions[0],
ResolutionPreset.high,
enableAudio: false,
);
await _cameraController.initialize().then((value) {
if (!mounted) {
return;
}
setState(() {
_initialized = true;
});
}).catchError((e) {
debugPrint(e);
});
}
@override
void dispose() {
_cameraController.dispose();
super.dispose();
}
@override
void initState() {
initCamera();
super.initState();
}
//in this build function the Exception occurs.
// Wrapping the "CameraPreview(_cameraController)," line in a try catch gives me working but ugly(?) code
@override
Widget build(BuildContext context) {
try {
return Scaffold(
body: Stack(
children: [
if(_initialized) CameraPreview(_cameraController),
if(!_initialized) Text("loading..."),
],
),
);
} catch (e) {
return const SizedBox();
}
}
}