I am working on a flutter widget which needs to load and update data in a Text with rest call. async call fetchPatientCount brings the data from REST resource and update the counter inside the setState method.

As a result of the below implementation, since the build method called twice, the counter value is NULL for the first time and causing the below exception.However for the second time the value is being populated.

 flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
    flutter: The following assertion was thrown building MainPage(dirty, state: _MainPageState#9e9d8):
    flutter: 'package:flutter/src/widgets/text.dart': Failed assertion: line 235 pos 15: 'data != null': is not
    flutter: true. 

Any help will be appreciated related to the issue.enter image description here

class MainPage extends StatefulWidget {
  @override
  _MainPageState createState() => _MainPageState();
}  

class _MainPageState extends State<MainPage> {
  String counter;

  @override
  void initState() {
    super.initState();
    fetchPatientCount().then((val) {
      setState(() {
        counter = val.count.toString();
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    String text;
    if(counter!=null) {
      text = counter;
    }

    return Scaffold(
        appBar: AppBar(
          elevation: 2.0,
          backgroundColor: Colors.white,
          title: Text('Dashboard',
              style: TextStyle(
                  color: Colors.black,
                  fontWeight: FontWeight.w700,
                  fontSize: 30.0)),
        ),
        body: StaggeredGridView.count(
          crossAxisCount: 2,
          crossAxisSpacing: 12.0,
          mainAxisSpacing: 12.0,
          padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
          children: <Widget>[
            _buildTile(
              Padding(
                padding: const EdgeInsets.all(24.0),
                child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          Text('Total Views',
                              style: TextStyle(color: Colors.blueAccent)),
                          Text(text,/* Here text is NULL for the first time */
                              style: TextStyle(
                                  color: Colors.black,
                                  fontWeight: FontWeight.w700,
                                  fontSize: 34.0))
                        ],
                      ),
                      Material(
                          color: Colors.blue,
                          borderRadius: BorderRadius.circular(24.0),
                          child: Center(
                              child: Padding(
                            padding: const EdgeInsets.all(16.0),
                            child: Icon(Icons.timeline,
                                color: Colors.white, size: 30.0),
                          )))
                    ]),
              ),
            ),
          ],
          staggeredTiles: [StaggeredTile.extent(2, 110.0)],
        ));
  }

  Widget _buildTile(Widget child, {Function() onTap}) {
    return Material(
        elevation: 14.0,
        borderRadius: BorderRadius.circular(12.0),
        shadowColor: Color(0x802196F3),
        child: InkWell(
            // Do onTap() if it isn't null, otherwise do print()
            onTap: onTap != null
                ? () => onTap()
                : () {
                    print('Not set yet');
                  },
            child: child));
  }
}

class PatientCount {
  int count;
  double amount;

  PatientCount({this.count, this.amount});
  PatientCount.fromJson(Map<String, dynamic> map)
      : count = map['count'],
        amount = map['amount'];
}

Future<PatientCount> fetchPatientCount() async {
  var url = "http://localhost:9092/hms/patients-count-on-day";

  Map<String, String> requestHeaders = new Map<String, String>();
  requestHeaders["Accept"] = "application/json";
  requestHeaders["Content-type"] = "application/json";

  String requestBody = '{"consultedOn":' + '16112018' + '}';

  http.Response response =
      await http.post(url, headers: requestHeaders, body: requestBody);

  final statusCode = response.statusCode;
  final Map responseBody = json.decode(response.body);

  if (statusCode != 200 || responseBody == null) {
    throw new Exception(
        "Error occured : [Status Code : $statusCode]");
  }
  return PatientCount.fromJson(responseBody['responseData']['PatientCountDTO']);
}


Solution 1: Günter Zöchbauer

fetchPatientCount().then((val) {
setState(() {
counter = val.count.toString();
});
});

That's expected behavior. "async" means the result will be available eventually later and then the code passed to then will be executed.

Flutter doesn't wait for that. It calls build() for every frame.

Perhaps you wanted to change

if(counter!=null) {
  text = counter;
}

to

if(counter!=null) {
  text = counter;
} else {
  text = 'waiting ...';
}

because otherwise text will be null and Text(null) causes the error you got.


Solution 2: Gazihan Alankus

If it's null, build a widget that says that it's loading. It will build the actual widgets in the second call that you mentioned.

Basically, do this:

 @override
  Widget build(BuildContext context) {
    String text;
    if(counter!=null) {
      text = counter;
    } else {
      return Text("loading..."); // or a fancier progress thing
    }


Solution 3: isudarsan

I solved my self, used FutureBuilder to resolve the issue. Here is the full code below.

class PatientCount {
  int count;
  double amount;

  PatientCount({this.count, this.amount});

  PatientCount.fromJson(Map<String, dynamic> map)
      : count = map['count'],
        amount = map['amount'];
}

Future<PatientCount> fetchPatientCount() async {
  var url = "http://localhost:9092/hms/patients-count-on-day";

  Map<String, String> requestHeaders = new Map<String, String>();
  requestHeaders["Accept"] = "application/json";
  requestHeaders["Content-type"] = "application/json";

  String requestBody = '{"consultedOn":' + '16112018' + '}';

  http.Response response =
      await http.post(url, headers: requestHeaders, body: requestBody);

  final statusCode = response.statusCode;
  final Map responseBody = json.decode(response.body);

  if (statusCode != 200 || responseBody == null) {
    throw new FetchPatientCountException(
        "Error occured : [Status Code : $statusCode]");
  }
  return PatientCount.fromJson(responseBody['responseData']['PatientCountDTO']);
}

class MainPage extends StatefulWidget {
  @override
  _MainPageState createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  @override
  void initState() {
    super.initState();        
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          elevation: 2.0,
          backgroundColor: Colors.white,
          title: Text('Dashboard',
              style: TextStyle(
                  color: Colors.black,
                  fontWeight: FontWeight.w700,
                  fontSize: 30.0)),
        ),
        body: StaggeredGridView.count(
          crossAxisCount: 2,
          crossAxisSpacing: 12.0,
          mainAxisSpacing: 12.0,
          padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
          children: <Widget>[
            _buildTile(
              Padding(
                padding: const EdgeInsets.all(24.0),
                child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          Text('Total Views',
                              style: TextStyle(color: Colors.blueAccent)),
                          /*Text(get,
                              style: TextStyle(
                                  color: Colors.black,
                                  fontWeight: FontWeight.w700,
                                  fontSize: 34.0))*/
                          buildCountWidget()
                        ],
                      ),
                      Material(
                          color: Colors.blue,
                          borderRadius: BorderRadius.circular(24.0),
                          child: Center(
                              child: Padding(
                            padding: const EdgeInsets.all(16.0),
                            child: Icon(Icons.timeline,
                                color: Colors.white, size: 30.0),
                          )))
                    ]),
              ),
            ),
          ],
          staggeredTiles: [StaggeredTile.extent(2, 110.0)],
        ));
  }

  Widget _buildTile(Widget child, {Function() onTap}) {
    return Material(
        elevation: 14.0,
        borderRadius: BorderRadius.circular(12.0),
        shadowColor: Color(0x802196F3),
        child: InkWell(
            // Do onTap() if it isn't null, otherwise do print()
            onTap: onTap != null
                ? () => onTap()
                : () {
                    print('Not set yet');
                  },
            child: child));
  }

  Widget buildCountWidget() {
    Widget vistitCount = new Center(
      child: new FutureBuilder<PatientCount>(
        future: fetchPatientCount(),
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return new Text(snapshot.data.count.toString(),
                style: TextStyle(
                    color: Colors.black,
                    fontWeight: FontWeight.w700,
                    fontSize: 34.0));
          } else if (snapshot.hasError) {
            return new Text("${snapshot.error}");
          }

          // By default, show a loading spinner
          return new CircularProgressIndicator();
        },
      ),
    );
    return vistitCount;
  }
}