I kept getting an error from the camera.dart that "name" was being called on a null object.
After some time, I realized that the problem was the that the build method is called before the async code in my initstate finished (I'm actually slightly proud that I understood the problem at least :))
I tried many different ways to initialize my camera properly, but I could not. This is the last iteration of my code.
What's the idiomatic way of handling this future?
class _PicturePreviewState extends State<PicturePreview> {
List<CameraDescription> cameras;
CameraDescription camera;
CameraController cameraController;
Future<void> initializeController;
Future<void> getCameras() async {
try {
cameras = await availableCameras();
} catch(e) {print(e);}
camera = cameras.last;
print(camera);
}
@override
void initState() {
super.initState();
// getCameras();
availableCameras().then((availableCameras) {
cameras = availableCameras;
camera = cameras.first;
cameraController = CameraController(
camera,
ResolutionPreset.low,
);
initializeController = cameraController.initialize();
print(cameraController.value.isInitialized);
});
// cameraController = CameraController(
// camera,
// ResolutionPreset.low,
// );
// initializeController = cameraController.initialize();
// print(cameraController.value.isInitialized);
}
@override
void dispose() {
cameraController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<void>(
future: initializeController,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the Future is complete, display the preview.
return CameraPreview(cameraController);
}
else {
// Otherwise, display a loading indicator.
print(snapshot.connectionState);
return Center(child: CircularProgressIndicator());
}
},
),
I have been relying on this page to use the camera package, but I could not use it verbatim because I can't keep passing down the camera object down my widget tree.
I fixed it. I put the initializing of the camera object in the in the parent of the widget.
class _TakeReceiptPictureState extends State<TakeReceiptPicture> {
List<CameraDescription> cameras;
CameraDescription camera;
@override
void initState() {
super.initState();
availableCameras().then((availableCameras) {
cameras = availableCameras;
camera = cameras.first;
});
}
Then made the widget that takes the picture have a parameter of type CameraDescription.
class PicturePreview extends StatefulWidget {
final CameraDescription camera;
const PicturePreview(this.camera, {Key key}) : super(key: key);
@override
_PicturePreviewState createState() => _PicturePreviewState();
}
Then passed the camera initialized in the parent to picture widget
onTap: () {
Navigator.of(context).push(
PageTransition(
type: PageTransitionType.transferRight,
child: PicturePreview(camera)),
);
}),
by the time the child widget's build method runs, the camera object is already initialized and ready to go.
Now the state of the child have only two variables, the camera controller and the initialize controller future.
CameraController cameraController;
Future<void> initializeController;
@override
void initState() {
super.initState();
cameraController = CameraController(
widget.camera,
ResolutionPreset.low,
);
initializeController = cameraController.initialize();
}
TLDR: let the initialization of the camera object be the responsibility of the parent of the widget.
A bit late to answer but this can be solved by using a boolean variable to check whether the camera has been initialized or not.
bool _isCameraInitialized = false;
setState()
statement.await _controller.initialize();
if (_controller.value.isInitialized) {
setState(() {
_isCameraInitialized= true;
})
}
Here, notice that the status of the controller is directly inferred from the controller itself using _controller.value.isInitialized
.
SizedBox()
.If the camera is not initialized then the build() function will produce a SizedBox and display it till the camera gets initialized and setState() is called.
Scaffold(
backgroundColor: Colors.white,
body: _isCameraInitialized
? //your UI here
: SizedBox(),
)
Do not that this is a very rudimentary approach to solving this problem. To build an app with good UX, you should consider including a loading screen of some sort using the boolean variable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With