Davidbeh
Davidbeh

Reputation: 53

Widget is created before it should be used => leads to error

I have a custom widget with a futurebuilder which I want to use to load some data.

This is how it looks like:

class LoadOverlay extends StatefulWidget {
  Widget child;
  LoadOverlay({Key key, this.child}) : super(key: key);

  @override
  _LoadOverlayState createState() => _LoadOverlayState();
}

class _LoadOverlayState extends State<LoadOverlay> {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: Catalog.catalog.loadAsset(), //Here the csvString gets loaded
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          if (snapshot.hasData)
            return widget.child;
          else {
            return Scaffold(
              body: Center(
                child: CircularProgressIndicator(), //Tells the user that something is loading
              ),
            );
          }
        }
    );
  }
}

And this is how i use it:

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return LoadOverlay(
        child: Scaffold(
          body: Center(
            child: Text(Catalog.catalog.csvString), //This gets created to early
        ),
      )
    );
  }
}

The Problem is that Text(Catalog.catalog.csvString) gets loaded before the futurebuilder executes and because csvString hasn't finished i get an null error. If i put this into the LoadOverlay like this

if (snapshot.hasData)
    Text(Catalog.catalog.csvString)

it works because the code gets only executed after the snapshot has data.

So how do I pass a Widget to the FutureBuilder that gets called and drawn AFTER the future was executed

Upvotes: 0

Views: 35

Answers (2)

Jigar Patel
Jigar Patel

Reputation: 5423

You can pass an empty String while csvString is still null.

child: Text(Catalog.catalog.csvString ?? ''),

OR

Assign an empty String to csvString when it is initialized.

String csvString = '';

Upvotes: 4

NiklasLehnfeld
NiklasLehnfeld

Reputation: 1080

A different solution could be to use a builder property in your custom widget instead of a child property. This would look like:

class LoadOverlay extends StatefulWidget {
  Widget Function(BuildContext) builder;
  LoadOverlay({Key key, this.builder}) : super(key: key);

  @override
  _LoadOverlayState createState() => _LoadOverlayState();
}

class _LoadOverlayState extends State<LoadOverlay> {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: Catalog.catalog.loadAsset(), //Here the csvString gets loaded
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          if (snapshot.hasData)
            return widget.builder(context);
          else {
            return Scaffold(
              body: Center(
                child: CircularProgressIndicator(), //Tells the user that something is loading
              ),
            );
          }
        }
    );
  }
}

And you would use it like:

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return LoadOverlay(
        builder: (context) => Scaffold(
          body: Center(
            child: Text(Catalog.catalog.csvString), //This gets created to early
        ),
      )
    );
  }
}

This way your code which you inject into the builder property gets only executed if the Future is finished.

Upvotes: 1

Related Questions