不使用 onPressed 刷新 StatefulBuilder 对话框
Refresh StatefulBuilder Dialog without using onPressed
我需要在加载报告时更新对话框的文本。 setState 在这里不起作用。
class ReportW extends StatefulWidget {
const ReportW({Key key}) : super(key: key);
@override
_ReportWState createState() => _ReportWState();
}
class _ReportWState extends State<ReportMenuDownloadW> {
String loadingText;
void updateLoadingText(text){
setState(() {loadingText = text;});
}
@override
Widget build(BuildContext context) {
return MyWidget(
label:REPORT_LABEL,
onTap: () async {
showDialog(context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) {
return Dialog(
child: Column(
children: [
CircularProgressIndicator(),
Text(loadingText),
],
),
);});
});
await loadPDF(context,updateLoadingText);
Navigator.pop(context);
},
);
}
}
如果不可能,是否有替代解决方案?我只需要在加载时在屏幕上显示一个进度文本指示器。
在您的情况下,您可以使用 GlobalKey。对于您的代码:
- 在您的小部件中定义 globalKey:
// Global key for dialog
final GlobalKey _dialogKey = GlobalKey();
- 为您的 StatefulBuilder 设置 globalKey:
return StatefulBuilder(
key: _dialogKey,
builder: (context, setState) {
return Dialog(
child: Column(
children: [
CircularProgressIndicator(),
Text(loadingText),
],
),
);
},
);
- 现在您可以像这样更新 UI 对话框:
void updateLoadingText(text) {
// Check if dialog displayed, we can't call setState when dialog not displayed
if (_dialogKey.currentState != null && _dialogKey.currentState!.mounted) {
_dialogKey.currentState!.setState(() {
loadingText = text;
});
}
}
注意,如果用户手动关闭对话框,您会遇到意外行为。
如何防止用户关闭对话框:在 showDialog 中使用 barrierDismissible: false
并使用 onWillPop: () async {return false;}
将对话框包装到 WillPopScope
可能的问题:
为什么我们检查 _dialogKey.currentState != null
?
因为打开对话框和设置 globalKey 需要一些时间,而当它没有打开时,currentState 为 null。如果 updateLoadingText
将在对话框打开之前被调用,我们不应该为对话框更新 UI。
您的小部件的完整代码:
class OriginalHomePage extends StatefulWidget {
OriginalHomePage({Key? key}) : super(key: key);
@override
_OriginalHomePageState createState() => _OriginalHomePageState();
}
class _OriginalHomePageState extends State<OriginalHomePage> {
String loadingText = "Start";
// Global key for dialog
final GlobalKey _dialogKey = GlobalKey();
void updateLoadingText(text) {
// Check if dialog displayed, we can't call setState when dialog not displayed
if (_dialogKey.currentState != null && _dialogKey.currentState!.mounted) {
_dialogKey.currentState!.setState(() {
loadingText = text;
});
}
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () async {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
key: _dialogKey,
builder: (context, setState) {
return Dialog(
child: Column(
children: [
CircularProgressIndicator(),
Text(loadingText),
],
),
);
},
);
},
);
await loadPDF(context, updateLoadingText);
Navigator.pop(context);
},
child: Text("Open"),
);
}
}
我也稍微重写了你的代码,在我看来更正确:
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
child: Text("Open"),
onPressed: () => _showDialog(),
),
),
);
}
// Global key for dialog
final GlobalKey _dialogKey = GlobalKey();
// Text for update in dialog
String _loadingText = "Start";
_showDialog() async {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return WillPopScope(
onWillPop: () async {
return false;
},
child: StatefulBuilder(
key: _dialogKey,
builder: (context, setState) {
return Dialog(
child: Padding(
padding: EdgeInsets.all(8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
Text(_loadingText),
],
),
),
);
},
),
);
},
);
// Call some function from service
await myLoadPDF(context, _setStateDialog);
// Close dialog
Navigator.pop(context);
}
// Update dialog
_setStateDialog(String newText) {
// Check if dialog displayed, we can't call setState when dialog not displayed
if (_dialogKey.currentState != null && _dialogKey.currentState!.mounted) {
_dialogKey.currentState!.setState(() {
_loadingText = newText;
});
}
}
}
结果:
Updated dialog
我需要在加载报告时更新对话框的文本。 setState 在这里不起作用。
class ReportW extends StatefulWidget {
const ReportW({Key key}) : super(key: key);
@override
_ReportWState createState() => _ReportWState();
}
class _ReportWState extends State<ReportMenuDownloadW> {
String loadingText;
void updateLoadingText(text){
setState(() {loadingText = text;});
}
@override
Widget build(BuildContext context) {
return MyWidget(
label:REPORT_LABEL,
onTap: () async {
showDialog(context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) {
return Dialog(
child: Column(
children: [
CircularProgressIndicator(),
Text(loadingText),
],
),
);});
});
await loadPDF(context,updateLoadingText);
Navigator.pop(context);
},
);
}
}
如果不可能,是否有替代解决方案?我只需要在加载时在屏幕上显示一个进度文本指示器。
在您的情况下,您可以使用 GlobalKey。对于您的代码:
- 在您的小部件中定义 globalKey:
// Global key for dialog
final GlobalKey _dialogKey = GlobalKey();
- 为您的 StatefulBuilder 设置 globalKey:
return StatefulBuilder(
key: _dialogKey,
builder: (context, setState) {
return Dialog(
child: Column(
children: [
CircularProgressIndicator(),
Text(loadingText),
],
),
);
},
);
- 现在您可以像这样更新 UI 对话框:
void updateLoadingText(text) {
// Check if dialog displayed, we can't call setState when dialog not displayed
if (_dialogKey.currentState != null && _dialogKey.currentState!.mounted) {
_dialogKey.currentState!.setState(() {
loadingText = text;
});
}
}
注意,如果用户手动关闭对话框,您会遇到意外行为。
如何防止用户关闭对话框:在 showDialog 中使用 barrierDismissible: false
并使用 onWillPop: () async {return false;}
WillPopScope
可能的问题:
为什么我们检查 _dialogKey.currentState != null
?
因为打开对话框和设置 globalKey 需要一些时间,而当它没有打开时,currentState 为 null。如果 updateLoadingText
将在对话框打开之前被调用,我们不应该为对话框更新 UI。
您的小部件的完整代码:
class OriginalHomePage extends StatefulWidget {
OriginalHomePage({Key? key}) : super(key: key);
@override
_OriginalHomePageState createState() => _OriginalHomePageState();
}
class _OriginalHomePageState extends State<OriginalHomePage> {
String loadingText = "Start";
// Global key for dialog
final GlobalKey _dialogKey = GlobalKey();
void updateLoadingText(text) {
// Check if dialog displayed, we can't call setState when dialog not displayed
if (_dialogKey.currentState != null && _dialogKey.currentState!.mounted) {
_dialogKey.currentState!.setState(() {
loadingText = text;
});
}
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () async {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
key: _dialogKey,
builder: (context, setState) {
return Dialog(
child: Column(
children: [
CircularProgressIndicator(),
Text(loadingText),
],
),
);
},
);
},
);
await loadPDF(context, updateLoadingText);
Navigator.pop(context);
},
child: Text("Open"),
);
}
}
我也稍微重写了你的代码,在我看来更正确:
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
child: Text("Open"),
onPressed: () => _showDialog(),
),
),
);
}
// Global key for dialog
final GlobalKey _dialogKey = GlobalKey();
// Text for update in dialog
String _loadingText = "Start";
_showDialog() async {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return WillPopScope(
onWillPop: () async {
return false;
},
child: StatefulBuilder(
key: _dialogKey,
builder: (context, setState) {
return Dialog(
child: Padding(
padding: EdgeInsets.all(8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
Text(_loadingText),
],
),
),
);
},
),
);
},
);
// Call some function from service
await myLoadPDF(context, _setStateDialog);
// Close dialog
Navigator.pop(context);
}
// Update dialog
_setStateDialog(String newText) {
// Check if dialog displayed, we can't call setState when dialog not displayed
if (_dialogKey.currentState != null && _dialogKey.currentState!.mounted) {
_dialogKey.currentState!.setState(() {
_loadingText = newText;
});
}
}
}
结果:
Updated dialog