Home > Software engineering >  FutureBuilder displays that data is null even tho it has value and still proceeds as normal [Flutter
FutureBuilder displays that data is null even tho it has value and still proceeds as normal [Flutter

Time:05-11

Hi currently Im working with local json file where I want to use some data from it. I have modeled it accordingly to that json file and when I print it out it works as normal. But when I run it in debug mode it stops at this points saying this :_CastError (type 'Null' is not a subtype of type 'List<UrlCheckModel>' in type cast)

This is my code:

FutureBuilder(
          future: readJsonData(),
          builder: (context, data) {
            if (data.hasError) {
              //in case if error found
              return Center(child: Text("${data.error}"));
            }
            List<UrlCheckModel> items = data.data as List<UrlCheckModel>;

Where readJsonData is:

Future<List<UrlCheckModel>> readJsonData() async {
    //read json file
    final jsondata =
        await rootBundle.rootBundle.loadString('jsonfile/malicious.json');
    //decode json data as list
    final list = json.decode(jsondata) as List<dynamic>;

    //map json and initialize using DataModel
    return list.map((e) => UrlCheckModel.fromJson(e)).toList();
  }

My question is why is this error happening even tho when I run the app it works fine (at brief 1 second time period the error appears) and how can I resolve this. Looking forward to your replies. (If needed I can post the whole code for this page).

CodePudding user response:

Ok so it seams even tho the json file is stored localy and Im not fetching it via API calls it needs to be initialized, so with help of @temp_ one viable solution is like this:

 if (data.data == null || data.hasError) {
              //in case if error found
              return const Center(
                child: Text(
                  'Please wait',
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    fontSize: 26,
                    color: Color.fromRGBO(255, 125, 84, 1),
                    fontWeight: FontWeight.bold,
                  ),
                ),
              );
            }

CodePudding user response:

First off

Before all, you have a error in your widget configuration:

FutureBuilder(
  // Do not call the async function directly
  future: readJsonData(),
  // ...
)

This will result on new Future being instantiated every rebuild, which kills device memory and your app will eventually be killed by the OS.

By the docs:

The future must have been obtained earlier, e.g. during State.initState, State.didUpdateWidget, or State.didChangeDependencies. It must not be created during the State.build or StatelessWidget.build method call when constructing the FutureBuilder. If the future is created at the same time as the FutureBuilder, then every time the FutureBuilder's parent is rebuilt, the asynchronous task will be restarted.

Then you should do something like this:

Future<List<UrlCheckModel>> _future;

void initState() {
  super.initState();

  _future = readJsonData();
}

Widget build(BuildContext context) {
  return FutureBuilder<List<UrlCheckModel>>(
    future: _future,
    // ..
  );
}

Now fixing the actual error

Since the rendering process is sync, which means Flutter can't wait your async process finish to render the first frame of your widget, you should be aware the data will be null in the first snapshot (AsyncSnapshot<T>.nothing()), in the docs:

For a future that completes successfully with data, assuming initialData is null, the builder will be called with either both or only the latter of the following snapshots:

  • AsyncSnapshot<String>.withData(ConnectionState.waiting, null)
  • AsyncSnapshot<String>.withData(ConnectionState.done, 'some data')

As you see, your 'data' is actually of type AsyncSnapshot<T>, then:

FutureBuilder<List<UrlCheckModel>>(
  future: _future,
  builder: (context, snapshot) {
    if (snapshot.connectionState != ConnectionState.done) {
      return Loading();
    }

    if (snapshot.hasError) {
      return HandleYourError();
    }

    List<UrlCheckModel> items = snapshot.data as List<UrlCheckModel>;

    return HappyEnd(items: items);
  }
);

See FutureBuilder<T> class reference for details

  • Related