I'm new to Flutter and I still struggle with some Future aspects. I have a bottom navigation bar, and switching between pages is done with a PageView. The problem is that when I switch to my "photo" page, it shows a Circular Progress Indicator FOREVER. I am a bit lost, honestly I have no idea what I'm doing wrong, it looks like the future builder never gets the value. Just to give more info: if I do an hot refresh, the camera starts to work and the picture appears.
This is the PhotoScreen page:
import 'dart:async';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
// A screen that allows users to take a picture using a given camera.
class PhotoScreen extends StatefulWidget {
const PhotoScreen({
Key key,
}) : super(key: key);
@override
PhotoScreenState createState() => PhotoScreenState();
}
class PhotoScreenState extends State<PhotoScreen> {
CameraDescription camera;
CameraController _controller;
Future<void> _initializeControllerFuture;
@override
void initState() {
super.initState();
// Obtain a list of the available cameras on the device.
inizializza();
}
@override
void dispose() {
// Dispose of the controller when the widget is disposed.
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Take a picture')),
// Wait until the controller is initialized before displaying the
// camera preview. Use a FutureBuilder to display a loading spinner
// until the controller has finished initializing.
body: FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the Future is complete, display the preview.
return CameraPreview(_controller);
} else {
// Otherwise, display a loading indicator.
return Center(child: CircularProgressIndicator());
}
},
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.camera_alt),
// Provide an onPressed callback.
onPressed: () async {
// Take the Picture in a try / catch block. If anything goes wrong,
// catch the error.
try {
// Ensure that the camera is initialized.
await _initializeControllerFuture;
// Attempt to take a picture and get the file `image`
// where it was saved.
final image = await _controller.takePicture();
} catch (e) {
// If an error occurs, log the error to the console.
print(e);
}
},
),
);
}
void inizializza() async {
final cameras = await availableCameras();
// Get a specific camera from the list of available cameras.
final firstCamera = cameras.first;
// To display the current output from the Camera,
// create a CameraController.
_controller = CameraController(
// Get a specific camera from the list of available cameras.
firstCamera,
// Define the resolution to use.
ResolutionPreset.medium,
);
// Next, initialize the controller. This returns a Future.
_initializeControllerFuture = _controller.initialize();
}
}
The inizializza method is actually asynchronous and you are executing it in the initState(), which is not. I think your inizializza method should be like this:
Future<void> inizializza() async {
final cameras = await availableCameras();
// Get a specific camera from the list of available cameras.
final firstCamera = cameras.first;
// To display the current output from the Camera,
// create a CameraController.
_controller = CameraController(
// Get a specific camera from the list of available cameras.
firstCamera,
// Define the resolution to use.
ResolutionPreset.medium,
);
// Next, initialize the controller. This returns a Future.
await _controller.initialize();
}
And that is the Future to be received by the FutureBuilder, instead of executing it in the initState()
FutureBuilder<void>(
future: inizializza(),
...
)