Manuel
Manuel

Reputation: 44

Automatically set State of Button WITHOUT pressing it

I have got a State Management Problem I couldn't get rid of and I want to reach out to you.

Basically, I activate with the Buttons a game and I am sending a String to the uC. The uC does its stuff and sends a response to Flutter including gameFinished=true (that works).

Now I want to reset the State of the Button to the init state WITHOUT pressing the Button. Following are some things I tried that didn't work.

@override
  void initState() {
    super.initState();
    setState(() {
      gameAktivated = false;
      gameStarted = false;
    });
  }
void asyncSetState() async {
    setState(() async {
      gameAktivated = false;
      gameStarted = false;
    });
  }

I am changing the style from "Start" to "Stop" when the Button is pressed and I send Data to the uC. (Works) Edit: Ofc I have a second button that triggers gameAktivated=true :)

ElevatedButton(
 onPressed: () {
  if (gameAktivated) {
   setState(() {
   gameStarted = !gameStarted;
  });
 if (gameStarted) {
  //Send Data to uC
 } else if (!gameStarted) {
  //Send Data to uC
 }
}
},
                    
child:
!gameStarted ? const Text('Start') : const Text('Stop'),
),

Button Displays Stop now. Following I am receiving a String from the uC that I jsonEncode and I receive gameFinished=true. (Works)

Container(
 child: streamInit
 ? StreamBuilder<List<int>>(
  stream: stream,
  builder: (BuildContext context,
  AsyncSnapshot<List<int>> snapshot) {
   if (snapshot.hasError) {
    return Text('Error: ${snapshot.error}');
   }
   if (snapshot.connectionState ==ConnectionState.active) {
    // getting data from Bluetooth
    var currentValue =const BluetoothConnection().dataParser(snapshot.data);
    config.jsonDeserializeGameFinished(currentValue);
    if(config.gameFinished){
     setState(() {
      gameAktivated = false;
      gameStarted = false;
     });
     asyncSetState();//Tested both methods seperate!
    }
    return Column(
     children: [
      Text(config.time.toString()),
     ],
    );
   } else {
    return const Text(
    'Check the stream',
    textAlign: TextAlign.center,
   );
  }
},
 ): const Text("NaN",textAlign: TextAlign.center,),
),

When I try to reset the state like in the code above this error occures: enter image description here

Calling setState Async didnt work for me either. Where and how can I set the state based on the response from the uC? Is it possible without using Provider Lib? Thanks in advance Manuel.

Upvotes: 0

Views: 450

Answers (3)

Manuel
Manuel

Reputation: 44

Answer to my own Question: In initState() added this:

stream.listen((event) {
 String valueJSON = const BluetoothConnection().dataParser(event);
 config.jsonDeserializeGameFinished(valueJSON);
 if (config.gameFinished) {
  setState(() {
   gameAktivated = false;
   gameStarted = false;
  });
 }
});

The Code above listens to the stream, UTF-8 Decodes and JSON-Decodes the data. After this you can access the variable to set a state.

Upvotes: 0

Aakash Kumar
Aakash Kumar

Reputation: 1187

Actually this error is not about the changing the state of button. Its a common mistake to update the widget state when its still building the widget tree.

Inside your StreamBuilder, you are trying to update the state before creating the UI which is raising this issue.

if(config.gameFinished){
     setState(() {
      gameAktivated = false;
      gameStarted = false;
     });

This will interrupt the build process of StreamBuilder as it will start updating the whole page. You need to move it out of the StreamBuilder's builder method.

To do that simply convert your stream to a broadcast, which will allow you to listen your stream multiple time.

var controller = StreamController<String>.broadcast();

Then inside the initState of the page you can setup a listener method to listen the changes like this

 stream.listen((val) => setState((){
      number = val;
    }));

Here you can change the state values because from here it will not interrupt the widget tree building cycle.

For more details see this example I created https://dartpad.dev/?id=a7986c44180ef0cb6555405ec25b482d

Upvotes: 1

Jannik
Jannik

Reputation: 730

If you want to call setState() immediately after the build method was called you should use:

 WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
     // this method gets called once directly after the previous setState() finishes.
 });

Upvotes: 0

Related Questions