Flutter:当我抛出异常时,Builder 在未来的构建器中调用两次
Flutter: Builder calls twice in future builder when i throw exception
在 initState 方法上创建我的未来方法实例后,我正在尝试在我未来的构建器上使用 snackbar 来显示 error.So:
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
以在构建器上获取错误并显示 snackbar。
I know future builder is for showing a widget per state but i want to
show snakbar.
但是生成器被调用了两次,snackbar 显示了两次错误?
我在两次构建中检查了这三个条件是否相同 snapshot.hasError and snapshot.error != null and snapshot.data == null
。我怎样才能避免显示两次 snakbar?
更新: 我分享的代码有问题。请检查一下。
首先,始终在 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'),
);
}
},
),
),
),
);
}
}
在 initState 方法上创建我的未来方法实例后,我正在尝试在我未来的构建器上使用 snackbar 来显示 error.So:
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
以在构建器上获取错误并显示 snackbar。
I know future builder is for showing a widget per state but i want to show snakbar.
但是生成器被调用了两次,snackbar 显示了两次错误?
我在两次构建中检查了这三个条件是否相同 snapshot.hasError and snapshot.error != null and snapshot.data == null
。我怎样才能避免显示两次 snakbar?
更新: 我分享的代码有问题。请检查一下。
首先,始终在 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'),
);
}
},
),
),
),
);
}
}