In your case you can use GlobalKey. For your code:
- Define globalKey inside your widget:
// Global key for dialog
final GlobalKey _dialogKey = GlobalKey();
- Set globalKey for your StatefulBuilder:
return StatefulBuilder(
key: _dialogKey,
builder: (context, setState) {
return Dialog(
child: Column(
children: [
CircularProgressIndicator(),
Text(loadingText),
],
),
);
},
);
- Now you can update UI of your dialog like this:
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;
});
}
}
Pay attention, you get unexpected behavior if user will close dialog manually.
How to prevent closing dialog by user: in showDialog use barrierDismissible: false
and also wrap your dialog to WillPopScope
with onWillPop: () async {return false;}
Possible question:
Why we check _dialogKey.currentState != null
?
Because opening dialog and set globalKey take some time and while it’s not opened currentState is null. If updateLoadingText
will be call before dialog will be open, we shouldn’t update UI for dialog.
Full code of your widget:
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"),
);
}
}
Also i rewrote your code a bit, it seems to me more correct:
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;
});
}
}
}
Result:
CLICK HERE to find out more related problems solutions.