1

I use a futureBuilder to display date inside TextFormFields, if there is data in the webservice I call in the futureBuilder for the date I selected in the DateTimePicker, the TextFormField is disabled and the data is displayed in it. Else, the textFormField is enabled.

I also have a button that I want to disable if there is data received and enable if there isn't, so I used a boolean.

Here is my code :

child: FutureBuilder<double?>(
                                  future: getTimes(selectedDate),
                                  builder: (BuildContext context, AsyncSnapshot snapshot) {
                                    if (snapshot.hasData){
                                      _timeController.clear();
                                        setState(() {
                                          _isButtonDisabled = false;
                                        });
                                      return TextFormField(
                                        controller: _timeController,
                                        textAlign: TextAlign.center,
                                        enabled: false,
                                        decoration: InputDecoration(
                                          hintText: snapshot.data.toString() + " h",
                                          contentPadding: EdgeInsets.zero,
                                          filled: true,
                                          fillColor: Colors.white70
                                        ),
                                      );
                                    } 
                                    else {
                                        setState(() {
                                          _isButtonDisabled = true;
                                        });
                                      return TextFormField(
                                        controller: _timeController,
                                        textAlign: TextAlign.center,
                                        enabled: true,
                                        decoration: InputDecoration(
                                          hintText: "0 h",
                                          contentPadding: EdgeInsets.zero,
                                          filled: true,
                                          fillColor: Colors.white
                                        ),
                                      );
                                    }
                                  }
                                )

This was causing me the error setState() or markNeedsBuild called during build , so thanks to the answers of this topic I encapsulated the setState method in WidgetsBinding.instance.addPostFrameCallback((_)

Here is what my code looks like now :

child: FutureBuilder<double?>(
                                  future: getTimes(selectedDate),
                                  builder: (BuildContext context, AsyncSnapshot snapshot) {
                                    if (snapshot.hasData){
                                      _timeController.clear();
                                      WidgetsBinding.instance?.addPostFrameCallback((_){
                                        setState(() {
                                          _isButtonDisabled = false;
                                        });
                                      });
                                      return TextFormField(
                                        controller: _timeController,
                                        textAlign: TextAlign.center,
                                        enabled: false,
                                        decoration: InputDecoration(
                                          hintText: snapshot.data.toString() + " h",
                                          contentPadding: EdgeInsets.zero,
                                          filled: true,
                                          fillColor: Colors.white70
                                        ),
                                      );
                                    } 
                                    else {
                                      WidgetsBinding.instance?.addPostFrameCallback((_){
                                        setState(() {
                                          _isButtonDisabled = true;
                                        });
                                      });
                                      return TextFormField(
                                        controller: _timeController,
                                        textAlign: TextAlign.center,
                                        enabled: true,
                                        decoration: InputDecoration(
                                          hintText: "0 h",
                                          contentPadding: EdgeInsets.zero,
                                          filled: true,
                                          fillColor: Colors.white
                                        ),
                                      );
                                    }
                                  }
                                )

The problem that I have now is my TextFormFields aren't clickable anymore, and the button is always enabled, may be a misused / misunderstood the addPostFrameCallback function.

Thanks for helping,

JS1
  • 631
  • 2
  • 7
  • 23

1 Answers1

0

You have DateTimePicker, after the selecting date-time you can call the future.

getTimes() returns nullable double. Before retuning data, compare value is null or not and set _isButtonDisabled based on it, assign true/false.

 bool _isButtonDisabled = true; // set the intial/watting state you want

 Future<double?> getTimes(DateTime time) async {
    //heavy operations
    return await Future.delayed(Duration(seconds: 3), () {
      return 4; //check with null +value
    });
  }

----
 @override
  Widget build(BuildContext context) {
    print("rebuild");
    return Column(
      children: [
        ElevatedButton(
          onPressed: () async {
            final selectedDate = await showDatePicker(
              context: context,
              initialDate: DateTime.now(),
              firstDate: DateTime.now().subtract(Duration(days: 4444)),
              lastDate: DateTime.now().add(Duration(days: 4444)),
            );

            if (selectedDate == null) return;

            final response = await getTimes(selectedDate);

            print(response);
            setState(() {
              _isButtonDisabled = response != null;
            });
          },
          child: Text("Select Date"),
        ),
        ElevatedButton(
            onPressed: _isButtonDisabled ? null : () {}, child: Text("t"))
      ],
    );}
Md. Yeasin Sheikh
  • 54,221
  • 7
  • 29
  • 56
  • 1
    Thanks for your answer, I tried it but the use of setState in the function create an infinite loop, I guess because the FutureBuilder is always calling this function to build the widget – JS1 Nov 09 '21 at 08:57
  • I've update the answer based on dialog, does it solve your issue? – Md. Yeasin Sheikh Nov 09 '21 at 09:46