0

I am trying to load some json data and have used a futurebuilder to wait for the data to be available first then display it. When I try to load the data in DetailsView, I get two log messages Saying there's no data and null, the screen errors out and after that returns back to normal and the json is logged to the console.

The exception thrown is The following NoSuchMethodError was thrown building FutureBuilder<String>(dirty, state: _FutureBuilderState<String>#cc31b): The method '[]' was called on null. Receiver: null.

Sample code:

body: Container(
      margin: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 15.0),
      child: FutureBuilder(
        future: DefaultAssetBundle.of(context).loadString("assets/my_data.json"), // validly referenced in pubspec and in right folder
        builder: (context, snapshot){
          if(!snapshot.hasData){
            print('no data'); // I understand it will be empty for now
          } else {
            print('hd');
          }
          var data = snapshot.data.toString();
          print(data); // null logged to console, is the future still empty at this point?
          final parsedData = json.decode(data);
          print('pd ${parsedData[1]}'); // this line is where it shows the method [] was called on null
          return data == null || data.isEmpty
              ? LoadingStrengths()
              : MyDataText();
              // : DailyStrengthText(day: widget.day, data: parsedData);
        },
      ),
    ),

The issues seem to be that:

  1. I am not handling the future properly
  2. I am not parsing the json properly, have attached a copy of it below.

Sample data:

[{
    "1": "Each day is an opportunity to start over again"
},
{
    "2": "Every man must at one point be tempted to hoist the black"
},
{
    "3": "Be kind to everyone you meet"
}]

I am trying to parse it by using the stringified numbers as key, to access the values. How do I make the model class for this or/and parse this json in the futurebuilder? Thanks.

------------------------------- EDIT ---------------------------------------- Here's the edited builder method, from which I was able to parse and print out the data:

if(!snapshot.hasData){
            print('no data yet');
            return LoadingStrengths();
          } else {
            print('hd');
            var data = snapshot.data.toString();
            parsedData = json.decode(data);
            print('pd ${parsedData[widget.day - 1]}');
          }
          return Text('${data[widget.day - 1]}'); // it errors out here

When I assign the value to the Text widget it errors out with this error message:

════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building FutureBuilder<String>(dirty, state: _FutureBuilderState<String>#cc19b):
The method '[]' was called on null.
Receiver: null
Tried calling: [](4)

The relevant error-causing widget was: 
FutureBuilder<String> file:///E:/FlutterProjects/daily_strength/lib/screens/DetailsView.dart:30:18
When the exception was thrown, this was the stack: 
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
#1      _DetailsViewState.build.<anonymous closure> 
(package:daily_strength/screens/DetailsView.dart:47:34)
#2      _FutureBuilderState.build 
(package:flutter/src/widgets/async.dart:751:55)
 #3      StatefulElement.build 
(package:flutter/src/widgets/framework.dart:4744:28)
#4      ComponentElement.performRebuild 
(package:flutter/src/widgets/framework.dart:4627:15)

Please what does this mean? I am getting the data but setting it to a text widget is not working for some reason? Thanks.

Nzadibe
  • 598
  • 2
  • 11
  • 28
  • Just after printing 'no data' add `return` – Dmytro Rostopira Dec 28 '20 at 08:31
  • I am returning two widgets even if there's no data. The problem is when I'm trying to print out the parsed json like parseddata[1]. Is this right? – Nzadibe Dec 28 '20 at 08:41
  • Does this answer your question? [What is a NoSuchMethod error and how do I fix it?](https://stackoverflow.com/questions/64049102/what-is-a-nosuchmethod-error-and-how-do-i-fix-it) – Dmytro Rostopira Dec 28 '20 at 09:02

4 Answers4

1

Create a separate Future Method ( add all the data processing here ) and, call it from Futurebuilder. This might be causing the issue. Also check if API is working, using Postman

jeugene
  • 211
  • 1
  • 3
1

You are calling data out of the if condition. Wrap it in the else condition like this

if (!snapshot.hasData){
    print('no data'); // I understand it will be empty for now
} else {
    print('hd');
    
    var data = snapshot.data.toString();
    print(data); // null wont be logged to console anymore
    final parsedData = json.decode(data);
    print('pd ${parsedData[1]}');
}
dm_tr
  • 4,265
  • 1
  • 6
  • 30
1

You are trying to decode the JSON when it's not loaded. Added some comments for explanation.

if(!snapshot.hasData) {
  // Problem is here, Data has not arrived yet. You are just printing there is
  // no data, but still moving on the next step
  print('no data');
} else {
  print('hd');
}
// data is null right now, toString() just return 'null' in string form
var data = snapshot.data.toString();

// null logged to console, is the future still empty at this point? yes exactly
print(data); 

// 'null' is parsed has null is returned
final parsedData = json.decode(data); 

// parsedData is null, you can't access using [], when it's null, that why exception is thrown
print('pd ${parsedData[1]}'); 

return data == null || data.isEmpty
              ? LoadingStrengths()
              : MyDataText();

Solution:

if(!snapshot.hasData) {
  return LoadingStrengths(); // return a loader when there is not data
} else {
  // Do stuff when it is loaded;
}
Talha Balaj
  • 309
  • 2
  • 12
  • Hi, your answer seems to make the most sense because of your approach. I have adapted the code to return the loading widget when there's no data in the future, it doesn't show up (how to fix that?) but at least it also doesn't error out. I want to return a Text(data) widget. Where do I do this? Thanks – Nzadibe Dec 28 '20 at 22:50
  • Hey, Thank you. Can you show a code snippet? – Talha Balaj Dec 29 '20 at 15:31
1

This is problematic:

future: DefaultAssetBundle.of(context).loadString("assets/my_data.json"),

This needs to be in a variable. Imagine this build() is called 30 times. The .loadString will be called 30 times, and your Future will never be completed!

Always put your Future into a simple var, and reference that simple var in your FutureBuilder. Don't put an action there.

Randal Schwartz
  • 39,428
  • 4
  • 43
  • 70