mobile - 避免 onPress 按钮中 setState 的小部件锁定?
问题描述
我想用 firebase 实现简单的登录/注销系统。我已经成功实现了登录/注销系统。登录时,用户将被定向到主屏幕,注销后,用户将被定向到登录屏幕(使用 RootScreen 处理进入主屏幕或登录)。然后我添加新页面设置屏幕并将注销过程移动到设置页面。因此,过程是用户登录 -> 主页(打开抽屉) -> 设置。但是,这样做之后,注销过程在设置屏幕中变为错误并收到此消息
“另一个例外:当小部件树被锁定时调用 setState() 或 markNeedsBuild()”
奇怪的是,如果我直接从主页到设置屏幕进行登录/注销过程,设置屏幕代码没有问题。
代码(RootScreen & SettingScreen):
import 'package:flutter/material.dart';
import 'login_screen.dart';
import 'home_screen.dart';
import 'setting_screen.dart';
import '../utilities/auth.dart';
class RootScreen extends StatefulWidget {
RootScreen({this.auth});
final BaseAuth auth;
@override
State<StatefulWidget> createState() => new RootState();
}
enum AuthStatus { notSignedIn, signedIn }
class RootState extends State<RootScreen> {
AuthStatus authStatus = AuthStatus.notSignedIn;
@override
void initState() {
super.initState();
widget.auth.currentUser().then((userId) {
setState(() {
authStatus =
userId == null ? AuthStatus.notSignedIn : AuthStatus.signedIn;
});
});
}
void signedIn() {
setState(() {
authStatus = AuthStatus.signedIn;
});
}
void signedOut() {
setState(() {
authStatus = AuthStatus.notSignedIn;
});
}
@override
Widget build(BuildContext context) {
switch (authStatus) {
case AuthStatus.notSignedIn:
return new LoginScreen(auth: widget.auth, onSignedIn: signedIn);
case AuthStatus.signedIn:
return new HomeScreen(auth: widget.auth, onSignedOut: signedOut);
}
}
}
2. Setting Screen & State:
import 'dart:async';
import 'package:flutter/material.dart';
import '../utilities/auth.dart';
class SettingScreen extends StatefulWidget {
SettingScreen({this.auth, this.onSignedOut});
final BaseAuth auth;
final VoidCallback onSignedOut;
final String title = "Setting";
@override
SettingState createState() => SettingState();
}
class SettingState extends State<SettingScreen> {
void signOut() async {
setState(() {
try {
widget.auth.signOut();
widget.onSignedOut();
} catch (e) {
print(e);
}
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Settings'),
actions: <Widget>[
new FlatButton(
child: Text('Logout',
style: new TextStyle(fontSize: 17.0, color: Colors.white)),
onPressed: signOut,
)
],
),
body: new Container(
child: new Center(
child: new Text('Settings', style: new TextStyle(fontSize: 32.0)),
)),
);
}
}
主屏幕:
class HomeScreen extends StatefulWidget {
HomeScreen({this.auth, this.onSignedOut});
final BaseAuth auth;
final VoidCallback onSignedOut;
@override
HomeState createState() => HomeState();
void signOut() async {
try {
await auth.signOut();
onSignedOut();
} catch (e) {
print(e);
}
}
}
//.. Inside the HomeState -> ListTile to Setting Menu
ListTile(
title:
Text('Pengaturan', style: new TextStyle(color: Colors.black)),
onTap: () {
// Update the state of the app
// ...
// Then close the drawer
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SettingScreen(
auth: widget.auth, onSignedOut: widget.onSignedOut)),
);
},
),
//..
在这种情况下,如何避免这种 setState 和小部件锁定异常?
解决方案
谢谢你基于 aziza 的评论,我通过在抽屉中添加两个 Navigator.pop(context) 来解决问题,然后进入其他菜单以避免小部件锁定,第二个在退出方法中,所以回到 RootScreen。
家庭状态抽屉:
//.. Inside the HomeState -> ListTile to Setting Menu
ListTile(
title:
Text('Pengaturan', style: new TextStyle(color: Colors.black)),
onTap: () {
// ...
// Add this
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SettingScreen(
auth: widget.auth, onSignedOut: widget.onSignedOut)),
);
},
),
//..
设定画面的登出方式:
void signOut() async {
setState(() {
try {
widget.auth.signOut();
widget.onSignedOut();
Navigator.pop(context);
} catch (e) {
print(e);
}
});
}
推荐阅读
- azure-data-factory - 在月底的第二个星期五以及同一周的星期二、星期三和星期四触发 Azure 数据工厂管道
- ip - 网关路由器如何为本地网络中的主机传送数据包(只有私有 IP 地址,如 192.168.2.101)?
- r - 如何暂时中止 R 的互联网连接
- mysql - 查找用户第一次和第二次收听之间的平均时间
- animation - 需要 SVG 动画指导
- .htaccess - 使用 301 重定向将旧站点重新映射到新站点 - 但重定向无法正常工作?
- javascript - 当事件名称包含点时如何拦截自定义事件?
- python - 将一条曲线水平移动到另一条曲线
- python - 调用 Python 对象时超出 UNET 分段训练最大递归深度
- c++ - 如何在 C++ 中使用前向声明