Search code examples
asynchronousflutterflutter-layoutsetstate

flutter setState, why the first output is null and how can i skip the output until it isn't null?


Answers.json

{
    "hello-world3-2": "19"
}

My game screen codes

String userAnswer = '';
var decodedAnswer;

class GameScreen extends StatefulWidget {
  @override
  _GameScreenState createState() => _GameScreenState();
}

class _GameScreenState extends State<GameScreen> {

here i get the answer which is 19 from the json file

  getDatas() async {
    final realAnswer = await rootBundle.loadString('lib/assets/answers.json');
    final decodedText = await json.decode(realAnswer.toString());
    decodedAnswer = decodedText["hello-world3-2"];

i want to make the decodedAnswer globally after it changes its value

    setState(
      () {
        while (decodedAnswer == null) {
          decodedAnswer = decodedText["hello-world3-2"];

loop again in case the decodedAnswer is still null

          continue;
        }
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    getDatas();
    print(decodedAnswer.toString());

Terminal output

Performing hot restart...                                               

Restarted application in 2,381ms.
W/Settings(25117): Setting device_provisioned has moved from android.provider.Settings.Secure to android.provider.Settings.Global.
V/HiTouch_HiTouchSensor(25117): User setup is finished.
V/AudioManager(25117): playSoundEffect   effectType: 0
V/AudioManager(25117): querySoundEffectsEnabled...
I/flutter (25117): null
I/flutter (25117): 19
I/flutter (25117): 19
I/flutter (25117): 19
I/flutter (25117): 19
I/flutter (25117): 19

Solution

  • You're thinking about this imperatively. Flutter is a declarative framework, meaning that you don't really want to control your application flow step-by-step, as you would with a piece of C code, for example. The setState method changes the state of your StatefulWidget and causes it to build again. You don't want to loop there and continually check whether the value has changed - that will only lead to your code freezing.

    Instead of thinking about this as: 1) create widget, 2) build widget, 3) check for data update 4) if data has not updated, repeat 3)

    You want to think about it this way: 1) create a widget, 2) start the data retrieval process, and when it's complete, change the state, 3) build my widget and let it figure out when to rebuild.

    So, you could do something like the following instead:

    class _GameScreenState extends State<GameScreen> {
      String _data;
    
      @override
      void initState() {
        super.initState();
        _data = '';
        getData().then((value) {
          setState(() {
            _data = value;
          });
        });
      }
    
      Future<String> getData() async {
        //do your processing
        return decodedText;
      }
    
      @override
      Widget build(BuildContext context) {
        pritn(_data);
        return Text(_data);
      }
    }