Search code examples
androidflutterdartmobilemobile-application

How to pass update UI value in buildResults of SearchDelegate?


I am trying to show real time http json data mtgCardNames using buildResults. The data seem to be passed down successfully (they are printed out correctly) but somehow they are not displayed on the screen. To understand the situation, I tried to display some other data (search query and testList), they show on the screen as expected.

Why is this happening?

I overrided all the required 4 methods, but only paste the buildResults here to be brief.

  @override
  Widget buildResults(BuildContext context) {
    List<MTGCard> mtgCards = [];
    List<Text> mtgCardNames = [];
    GetHTTP getHTTP = GetHTTP();
    getHTTP.getData().then((usersFromServer) {
      mtgCards = usersFromServer;
      for (MTGCard c in mtgCards){
        print(c.name);    // <= can print out as expected
        mtgCardNames.add(Text(c.name));
      }
    });
    List<Text> testList = [
      Text('a'),
      Text('aa'),
      Text('aaa'),
    ];

    return Column(
      children: <Widget>[
        Text(query),
        Column(
          children: mtgCardNames,
        ),
        Column(
          children: testList,
        ),
      ],
    );
  }

UI

For this moment, the http data is not based on the query, it is hard coded url but real time data. I am running on Ubuntu and my flutter doctor shows no issue.

Any suggestion or help is highly appreciated!


Solution

  • The problem is that network call getData() is executed asynchronously:

    1) You call getData()

    2) Synchronous code continues to execute: return Column(...) is called while mtgCardNames is still empty

    3) You see empty column on the screen

    4) Network call finishes, then(...) is executed and mtgCardNames is populated


    To actually display column with data from network call, you should return one widget while it's loading and another one after. One way is to use FutureBuilder. Basic idea is

    @override
    Widget buildResults(BuildContext context) {
      GetHTTP getHTTP = GetHTTP();
      return FutureBuilder<List<MTGCard>>(
        future: getHTTP.getData(),
        builder: (context, snapshot) {
          if (!snapshot.hasData)
            return Text('Loading...');
          return Column(
            children: <Widget>[
              Text(query),
              for (MTGCard c in snapshot.data)
                Text(c.name),
            ],
          );
        },
      );
    }
    

    But there are some more details. E.g. it’s not a good idea to put network call in a build() method. Also you can show some error widget if network call fails. See details in tutorial