如何等待异步函数正确完成(flutter,dart)

huangapple go评论107阅读模式
英文:

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 &#39;package:flutter/material.dart&#39;;
import &#39;package:camera/camera.dart&#39;;

class CameraPreviewPage extends StatefulWidget {
  const CameraPreviewPage({super.key});

  @override
  State&lt;CameraPreviewPage&gt; createState() =&gt; _CameraPreviewPageState();
}

class _CameraPreviewPageState extends State&lt;CameraPreviewPage&gt; {
  late List&lt;CameraDescription&gt; _cameraDescriptions;
  late CameraController _cameraController; //Here I declare the camera object which throws an &quot;late not inizialized&quot; 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 &quot;CameraPreview(_cameraController),&quot; 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编码中的&quot;更改为正常的双引号以进行更清晰的阅读。

英文:

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&lt;CameraPreviewPage&gt; {
  late List&lt;CameraDescription&gt; _cameraDescriptions;
  late CameraController _cameraController; //Here I declare the camera object which throws an &quot;late not inizialized&quot; 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 &quot;CameraPreview(_cameraController),&quot; 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(&quot;loading...&quot;),
          ],
        ),
      );
    } catch (e) {
      return const SizedBox();
    }
  }
}

huangapple
  • 本文由 发表于 2023年7月13日 22:08:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76680320.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定