TaylorR
TaylorR

Reputation: 4013

Invalid arguments: The source must not be null

I had this app that fetches some data from a remote API. So the data that I am going to receive and display in a JSON Future: {"status":200,"out":{"summary":[{"bc":"1876","wc":"488679","pc":"731904"}],"last":[{"id":"1877","place":"7","publisher":"-1","bookid":"01877","title":"Neither Civil Nor Servant","author":"Peh","region":"\u65b0\u52a0\u5761","copyrighter":"","translated":"0","purchdate":"2017-04-18","price":"200.00","pubdate":"2016-01-01","printdate":"2016-01-01","ver":"1.1","deco":"\u666e\u901a","kword":"0","page":"220","isbn":"978-981-4642-63-7","category":"","location":"","intro":"TT\u8d60\u4e66\u3002","instock":"1","p_name":"\uff08\u672a\u6307\u5b9a\uff09"}]}}

I will extract the out field from this JSON and assing summary and last to two variables:

initState() async {
    var getter = createHttpClient();
    String uri='http://api.rsywx.com/book/summary';
    var res=await getter.get(uri);
    Map data=JSON.decode(res.body);

    var out=data['out'];

    setState(() {
      _today=formatDate(new DateTime.now());
      _lb=out['last'][0];
      _bs=out['summary'][0];
      _lb['purchdate']=formatDate(DateTime.parse(_lb['purchdate']));
    });
  }

So _bs and _lb are all compound objects.

In my widget build function, I will display the contents of these two objects:

 new TextSpan(
     text: numFormatter.format(int.parse(_bs['bc'])),
     style: aboutTextStyle,
 ),

The program compiles OK but when launched, a quick splash RED screen will appear:

enter image description here

And soon enough, the correct screen will appear:enter image description here

I know that during the initial build, the object _bs, _lb is not there yet and the async call to a remote API is still trying to populate the returned response, so in this case, _bs['bc'] will definitely be not callable. Thus the non-blocking error pops out.

Workaround

I can eliminate this error by declaring a bunch of variables and assign them in the initState function; instead of rendering _bs['bc'], I will render a new variable _bookCoount. This way, the rendering will be done without this RED screen and the value of that variable will initially be null and soon be the correct value fetched from remote API.

But this is too cumbersome, if you get what I mean: A lot of used-only-once variables.

Or, shall I make the data fetched on the parent level, so that it will be passed to this widget as props? Not tried yet.

Would appreciate your best practice input.

Update

The issue really comes from int.parse. If I took out that call, the program runs peacefully.

So the question now becomes

I would suppress int.parse prompting an error before the value it is going to parse becomes valid.

Upvotes: 4

Views: 7999

Answers (1)

Günter Zöchbauer
Günter Zöchbauer

Reputation: 657546

Not sure what you mean with your workaround. In your example setState() won't be called before await getter.get(uri); returns a value.

I guess this should do

 new TextSpan(
     text: _bs != null && _bs['bc'] != null ? [numFormatter.format(int.parse(_bs['bc'])) : 0,
     style: aboutTextStyle,
 ),

Upvotes: 2

Related Questions