setState not working anymore if page reloads - flutter

I'm using the package speech_to_text in order to create a voice recognition page.
On the first load of the page, everything seems to be right and the package works perfectly.
If I go back and reopen the voice recognition page, the setState function doesnt' work anymore.
So I worked to put the code in a custom Dialog() of my CustomDialog() class that id builded from the MainPage() class.
I made this in order to use a single Stateful Widget, otherwise I couldn't update the dialog content.
In this way I get the same issue, since in the app there are direct link to the MainPage().
This will rebuild the MainPage() and when I call my CustomDialog(), the setState() is not working anymore.
Here is the code:
void _listen() async {
if (!_isListening) {
bool available = await _speech.initialize(
finalTimeout: const Duration(seconds: 10),
onStatus: (val) {
print('onStatus: $val');
if (val.contains("done")) {
print('>> STATUS DONE');
setState(() {
_isListening = false;
});
}
},
onError: (val) {
print('onError: $val');
},
);
if (available) {
finale = false;
setState(() {
_isListening = true;
stop = false;
finale = false;
});
_speech.listen(
listenMode: stt.ListenMode.confirmation,
onResult: (val) => setState(() {
_text = val.recognizedWords;
if (val.hasConfidenceRating && val.confidence > 0) {
//_confidence = val.confidence;
}
}),
);
}
} else {
setState(() {
_isListening = false;
});
_speech.stop();
}
}
You can see that there is a print value: "STATUS DONE" and this works.
But the setState() after that, is not working.
Any workaround?

The setState method is used to rebuild UI for Stateful widgets. However, when you open a dialog box, you have to keep in mind that it is itself not a stateful widget. You can use the StatefulBuilder widget to overcome this problem:
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: Text("Title of Dialog"),
content: Text(contentText),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context),
child: Text("Cancel"),
),
TextButton(
onPressed: () {
setState(() {
contentText = "Changed Content of Dialog";
});
},
child: Text("Change"),
),
],
);
},
);

Related

How to check Alert Dialog is open only one times instead of multiple new dialog box after onTap in flutter

I am working on my flutter application and I want to check whether the alert dialog is open or not on the screen . Can anyone tell me how to do that, now everytime i press ontap and it will appear a new dialog box. how can i only appear one dialog box instead of multiple of new dialog box ?
I have try bool, ontap cancel all not working.
Future? _dialog;
Future<void> _checkTimer() async {
if (_dialog == null) {
_dialog = await Future.delayed(Duration(seconds: 5));
showTimer(context);
await _dialog;
_dialog = null;
} else {
//do nothing
}
}
showTimer(BuildContext context) {
// set up the buttons
// ignore: deprecated_member_use
if (didUserTouchedScreen = true){
Container alert = Container(child: _imageslideshowProductDetailstimer());
// show the dialog
showDialog(
barrierDismissible: true,
context: context,
builder: (BuildContext context) {
return WillPopScope(
onWillPop: () async {
didUserTouchedScreen = false;
// _checkTimer();
return true;
},
child: alert);
},
).then((_) => didUserTouchedScreen = false);
}}
behavior: HitTestBehavior.translucent,
onTapDown: (tapdown) {
print("down");
_checkTimer();
},
onTapCancel: (){print('up');_checkTimer();}
You can achieve this with a boolean state, let's call it isButtonActive. The button is enabled/disabled depending on the value of this state. When the button is pressed, set the state to false, and when the dialog box is closed, set the state to true.
Below is an example code:
class _HomePageState extends State<HomePage> {
bool isButtonActive = true;
showTimer(BuildContext context) async {
setState(() {
isButtonActive = false;
});
await Future.delayed(Duration(seconds: 2));
showDialog(
context: context,
builder: (BuildContext context) {
return Column(
children: const [
Text('qwerty'),
],
);
},
).then((value) {
setState(() {
isButtonActive = true;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('총톤수'),
),
body: Center(
child: ElevatedButton(
onPressed: isButtonActive ? () => showTimer(context) : null,
child: const Text('총톤수'),
),
),
);
}
}

How to execute a function when a condition is met in Flutter/Dart?

I have a function:
//Alert Dialog about questions and answers
void _showAlertDialog() {
// set up the buttons
Widget Answer1Button = TextButton(
child: Text(_listData[_listCount][3]),
onPressed: () {},
);
Widget Answer2Button = TextButton(
child: Text(_listData[_listCount][4]),
onPressed: () {},
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
// title: Text(),
content: Text(_listData[_listCount][2]),
actions: [
Answer1Button,
Answer2Button,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
It works great when I click on the button in the different place for testing. But how to make it run under the following condition:
void _nextCSV() {
setState(() {
_listData = _listData;
_listData[_listCount][2] == "" ? _showAlertDialog : print('False');
});
}
Thanks in advance.
You are missing the ()
void _nextCSV() {
setState(() {
_listData = _listData;
_listData[_listCount][2] == "" ? _showAlertDialog() : print('False');
});
}
But even inside the setState you can use if in case you want to.
Edited:
In case you want to ignore the then or the else block you could just put null.
condition ? null : result;
condition ? result : null;

Flutter how to ensure that a dirty widget has been re-built before continuing with execution

I am trying to take a screenshot after hiding a few parts of the UI. I can not figure out how to assure that the UI parts are hidden before I take the screenshot except using Future.delayed(). SetState only marks the widget to be re-built. How can I know when it has been?
floatingActionButton: FloatingActionButton(
child: Icon(Icons.camera, color: Colors.white, size: 45.0),
onPressed: () {
takeScreenCapture();
},
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
void takeScreenCapture() async {
if(mounted) {
setState(() {
hideUi = true;
});
}
// need a way to wait until the above change is reflected in the UI
// before exceuteing the command below
String path = await NativeScreenshot.takeScreenshot();
if(mounted) {
setState(() {
_hideUi = false;
});
}
Navigator.push(context, MaterialPageRoute(builder: (context) => ImageDisplay(path)));
}
If I delay the screenshot just a little bit the UI has time to repaint and it works but this is not something I would want in production code:
void takeScreenCapture() async {
Future.delayed(Duration(milliseconds: 100), () async {
String path = await NativeScreenshot.takeScreenshot();
setState(() {
_hideUi = false;
});
Navigator.push(context, MaterialPageRoute(builder: (context) => ImageDisplay(path)));
});
}
not sure if I understood correctly but try something like this
void takeScreenCapture() async {
setState(() {
_hideUi = false;
WidgetsBinding.instance.addPostFrameCallback((_) async {
String path = await NativeScreenshot.takeScreenshot();
Navigator.push(context, MaterialPageRoute(builder: (context) => ImageDisplay(path)));
});
});
}
#Dude had the right idea with WidgetsBinding.instance.addPostFrameCallback, just posting below what would work for my example:
void takeScreenCapture() async {
WidgetsBinding.instance.addPostFrameCallback((_) async {
String path = await NativeScreenshot.takeScreenshot();
Navigator.push(context, MaterialPageRoute(builder: (context) => ImageDisplay(path)));
setState(() {
_hideUi = false;
});
});
}

Flutter Modular Mobx - Observable doesn't update inside of onpress

I am new with Flutter and I am having a problem. I'm using mobx. In my view I have a button and inside of this button, I am waiting for the showDialog property to change in order to show the dialog view . However, within onpress the showdialog does not work. Is there any other way to do this?
My controller
#observable
bool showDialog = false;
#action
Future callLoginService() async {
await Future.delayed(Duration(seconds: 6));
showDialog = true;
}
view
Observer(
builder: (_) {
return Center(
child: RaisedButton(
child: Text("TESTE"),
onPressed: () async {
controller.callLoginService();
if (controller.showDialog) {
final action = await InfoDialogView.showAlertDialog(
context, "Try again", 'Invalid user');
if (action == DialogAction.abort) {
controller.showDialog = false;
}
}
},
),
);
},
),
This is because your onPressed method is asynchronous but you haven't used 'await' keyword ahead of controller.callLoginService().
Observer(
builder: (_) {
return Center(
child: RaisedButton(
child: Text("TESTE"),
onPressed: () async {
await controller.callLoginService(); //put await for calling asynchronous methods
if (controller.showDialog) {
final action = await InfoDialogView.showAlertDialog(
context, "Try again", 'Invalid user');
if (action == DialogAction.abort) {
controller.showDialog = false;
}
}
},
),
);
},
),

Flutter : Waiting for response of an alert dialog inside onWillAccept function of DragTarget

I have recently started learning Flutter and I am stuck at a point while dealing with Drag and Drop using Droppable and DragTarget. When I Drag my draggable element over DropTarget element, I am doing few validations in onWillAccept method. One of the conditions here requires me to confirm with user if they are willing to continue with their action before returning true and heading to onAccept method. For some reason, code execution does not wait for user's action to return.
This is how my DragTarget looks
DragTarget<Map>(
builder: (context, listOne, listTwo) {
return Container();
},
onWillAccept: (value) {
if(condition1) {
return true;
} else if(condition2) {
return true;
} else {
if(!condition3) {
return true;
} else {
await _showConfirmation();
return false;
}
}
},
onAccept: (value) {
print(value);
},
)
and _showConfirmation method looks something like this
Future<void> _showConfirmation() async {
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Attention'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('Some message")
],
),
),
actions: <Widget>[
FlatButton(
child: Text('Accept'),
onPressed: () {
Navigator.of(context).pop();
return true;
},
),
FlatButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
return false;
},
)
],
);
},
);
}
Adding await does not help as onWillAccept is not async. Making it async did not help either.
_showConfirmation().then((result) {
return result
})
Above code did not help either. In many cases, the dragged item is left hanging over DragTarget box.
Any help regarding this will appreciated, thank you.
What happens here is that _showConfirmation() returns a Widget instead of a boolean - which what seems you're expecting from the snippet provided. The current setup lets false to be returned without waiting for the boolean value.
await _showConfirmation();
return false;
Instead of _showConfirmation() returning a Widget and proceeding to return false, you can wait for the value to be returned by using then().
Change _showConfirmation() to return a boolean
Future<bool> _showConfirmation() async {
...
}
then call await before returning.
return await _showConfirmation();