英文:
How do I wait for an asynchronous function to finish correctly (flutter, dart)
问题
我想在Flutter应用程序中创建一个屏幕,用于显示相机预览。为了实现这个目标,我必须异步初始化相机。这会导致一个异常,因为我在相机初始化之前就访问了它。我通过使用try-catch块来解决这个问题。但是这让我觉得有点笨拙。是否有更好的解决办法?我在Windows上为Android开发。以下是该屏幕的完整代码。我用行注释标记了感兴趣的点。
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
class CameraPreviewPage extends StatefulWidget {
const CameraPreviewPage({Key? key});
@override
State<CameraPreviewPage> createState() => _CameraPreviewPageState();
}
class _CameraPreviewPageState extends State<CameraPreviewPage> {
late List<CameraDescription> _cameraDescriptions;
late CameraController _cameraController; //在这里声明相机对象,会引发“late not initialized”异常
//这个函数异步初始化相机对象
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.toString());
});
}
@override
void dispose() {
_cameraController.dispose();
super.dispose();
}
@override
void initState() {
initCamera();
super.initState();
}
//在这个build函数中发生异常。
// 尝试使用try-catch块包装"CameraPreview(_cameraController),"行可以让我得到工作的但可能有点丑的代码
@override
Widget build(BuildContext context) {
try {
return Scaffold(
body: Stack(
children: [
CameraPreview(_cameraController),
],
),
);
} catch (e) {
return const SizedBox();
}
}
}
我对异步编程和Flutter/Dart本身都比较新手。如果这个问题在其他地方已经得到了回答,请引导我去那里,因为我在Google/YouTube上找不到相关信息。
我尝试过使用FutureBuilder小部件,但它没有正常工作,也许是因为我没有理解它,但不幸的是我丢失了那段代码。
英文:
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.
答案1
得分: 1
问题在于initState
本身不是一个异步方法,无法等待您的initCamera
。因此,您的initCamera
在调用build
方法时执行。
然后,您的_cameraController
尚未初始化,但在build
方法中使用。
要解决此问题,您可以根据状态布尔值有条件地显示不同的小部件,或者改用FutureBuilder
小部件。
在使用布尔值时的状态类:
class _CameraPreviewPageState extends State<CameraPreviewPage> {
late List<CameraDescription> _cameraDescriptions;
late CameraController _cameraController; // 这里我声明相机对象,会引发“late not initialized”异常
bool _initialized = false;
// 这个函数异步地初始化对象
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();
}
// 在这个构建函数中出现异常。
// 尝试将“CameraPreview(_cameraController)”行包装在try catch中,可以使代码工作,但看起来有点丑陋(?)。
@override
Widget build(BuildContext context) {
try {
return Scaffold(
body: Stack(
children: [
if(_initialized) CameraPreview(_cameraController),
if(!_initialized) Text("loading..."),
],
),
);
} catch (e) {
return const SizedBox();
}
}
}
请注意,我已经将HTML编码中的"
更改为正常的双引号以进行更清晰的阅读。
英文:
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();
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论