首页 > 解决方案 > Flutter:当我抛出异常时,构建器在未来的构建器中调用了两次

问题描述

我试图在我未来的构建器上使用snackbar来显示错误。所以在initState方法上创建我未来的方法实例之后:

Future<DetailModel> futureDetail;
  ...
   @override
  void initState() {
    futureDetail =
        DetailProvider().detailProvider(widget.id);
    super.initState();
  }

在 futureBuilder 中,我使用如下:

Center(
              child: SingleChildScrollView(
                child: FutureBuilder(
                  future: futureDetail,
                  builder:
                      (context, AsyncSnapshot<DetailProductModel> snapshot) {
                    if (!snapshot.hasError ||
                        snapshot.connectionState == ConnectionState.waiting) {
                      return Center(
                        child: CircularProgressIndicator(),
                      );
                    }
                    if (snapshot.hasData && snapshot.data.data != null) {
                      return Container();
                    }
                    if (snapshot.hasError &&
                        snapshot.error != null &&
                        snapshot.data == null) {
                      WidgetsBinding.instance.addPostFrameCallback((_) =>
                          showSnackbar(snapshot.error.toString(), context));
                      return Container();
                    } else {
                      return Center(
                        child: Text('failed to load'),
                      );
                    }
                  },
                ),
              ),
            ),

detailProvider方法调用 http 服务,在我的示例中,我抛出 SocketException生成器错误并显示快餐栏。

我知道未来的构建器是为每个状态显示一个小部件,但我想显示 snakbar。

但是builder被调用了两次并且snackbar显示了两次错误?snapshot.hasError and snapshot.error != null and snapshot.data == null我在两次 build 中检查了这三个条件是否相同。我怎样才能防止显示两次 snakbar?

标签: flutterflutter-layoutfuture

解决方案


更新:我分享的代码有问题。请检查一下。

首先,始终在 FutureBuilder 之外创建您的 SnackBar,因为 FutureBuilder 会不断被调用,因此会导致无限循环。你可以做的是使用 catchError。您仍然可以使用我们已经声明的变量在创建新的 SnackBar 之前检查是否存在活动的 SnackBar。

你会看到 SnackBar 只会显示一次,即使我在运行下面的代码时调用了三次。

应用相同的方式来更新您自己的代码:

class App extends StatefulWidget {
  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {
  bool snackBarActive = false;
  Future<String> futureDetail;

  @override
  void initState() {
    futureDetail = Future<String>.error('An error Occurred').catchError((errText) {
      showSnackBar(errText);
      showSnackBar(errText);
      showSnackBar(errText);
    });
    super.initState();
  }

  void showSnackBar(String errText) {
    if (!snackBarActive) {
      setState(() {
        snackBarActive = true;
      });

      WidgetsBinding.instance.addPostFrameCallback((_) => Scaffold.of(context)
              .showSnackBar(SnackBar(
                content: Text(errText),
              ))
              .closed
              .whenComplete(() {
            setState(() {
              snackBarActive = false;
            });
          }));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: SingleChildScrollView(
          child: FutureBuilder(
            future: futureDetail,
            builder: (context, AsyncSnapshot<String> snapshot) {
              if (!snapshot.hasError &&
                  snapshot.hasData &&
                  snapshot.data != null) {
                return Container();
              } else {
                return Center(
                  child: Text('failed to load'),
                );
              }
            },
          ),
        ),
      ),
    );
  }
}



推荐阅读