I need to redraw my UI whenever the app receives data from a serial device (using the usb_serial package).
TextEditingController cntrl = TextEditingController();
...
ListView(
children:[
TextButton(
onPressed: () async {
port = await _deviceList[0].create();
bool? openResult = await port?.open();
if (!openResult!) {
SmartDialog.showToast("Failed to connect!");
return;
}
port?.setPortParameters(
globals.baudRate,
globals.dataBits,
globals.stopBits,
globals.parity,
);
SmartDialog.showToast("Connected!");
port?.inputStream?.listen((Uint8List event) {
setState(() {
_cntrl.text=utf8.decode(event);
});
});
},
child: const Text("Start"),
style: startButtonStyle,
),
TextField(
controller: _cntrl,
),
],
)
but the UI is never redrawn. I searched a lot on the web on what should be done if I have to call setState() from a child event but couldn't find any answers. What could be the solution?
Edit: Apparently it was just some weird bug, I copied the same code to a new project and it started working like a charm.
CodePudding user response:
An easy way is to pass a function to the child, for example:
class MyChildWidget extends StatelessWidget {
final VoidCallback onRefresh; // The callback that will be called to refresh parent state
const MyChildWidget({Key? key, required this.onRefresh}) : super(key: key);
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => onRefresh(), // Call the callback when button is tapped
child: Text('Refresh Parent'),
);
}
}
Now when using the MyChildWidget
, you can pass in onRefresh
parameter and let that do setState
in the parent, for example:
MyChildWidget(
onRefresh: () => setState(() {}),
);
This would be an easy way to do some simple state management. When things get too complicated, you could look into some more advanced state management solutions such as StreamBuilder
or the provider
package.
Update:
With your updated question, the problem is not at set state
. Maybe the your input stream
did not emit events. You could try to add print
statements to check if the stream is working.
Try this much shorter code to test the concept of "set state to a TextField", and you should see it's working as intended:
final _controller = TextEditingController();
// ...
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: () => setState(() {
_controller.text = 'Hello World';
}),
child: const Text('Set text'),
),
TextField(
controller: _controller,
),
],
)