Robert Robert - 4 months ago 17
Dart Question

flutter timing problems on stateful widget after API call

I am running into timing problems, I am getting data from a api, then creating a list from the JSON. I think use the length of the resulting list as the item count in my listview. However it throws a null error on the itemcount and then completes processing and presents the listview. I am trying to find where the timing problem is and how items and widgets are processed so that I can avoid the error. My code is below if anyone has any ideas where my code is flawed.

class Specialty extends StatefulWidget {
Specialty({Key key, this.title}) : super(key: key);

final String title;

@override
_SpecialtyState createState() => new _SpecialtyState();
}

class _SpecialtyState extends State<Specialty> {

bool _dataReceived = false;
bool _authenticated = false;
SharedPreferences prefs;
List mylist;


@override
void initState() {
super.initState();

_getPrefs();
_getSpecialty();
}


_getPrefs() async {
prefs = await SharedPreferences.getInstance();
_authenticated = prefs.getBool('authenticated');
print('AUTH2: ' + _authenticated.toString());
print('AUTHCODE2: ' + prefs.getString('authcode'));

}

_getSpecialty() async {
var _url = 'http://174.138.61.246:8080/support/specialty';

var http = createHttpClient();
var response = await http.get(_url);

var specialties = jsonCodec.decode(response.body);

mylist = specialties.toList();
//_dataReceived = true;


setState(() {
_dataReceived = true;
});
}

Future<Null> _onRefresh() {
Completer<Null> completer = new Completer<Null>();
Timer timer = new Timer(new Duration(seconds: 3), () {
completer.complete();
});
return completer.future;
}

@override
Widget build(BuildContext context) {
return new Scaffold(
body: new RefreshIndicator(
child: new ListView.builder(
itemBuilder: _itemBuilder,
itemCount: mylist.length,
),
onRefresh: _onRefresh,

));
}

Widget _itemBuilder(BuildContext context, int index) {
Specialties spec = getSpec(index);
return new SpecialtyWidget(spec: spec,);
}

Specialties getSpec(int index) {
return new Specialties(
mylist[index]['id'], mylist[index]['name'], mylist[index]['details'],
new Photo('lib/images/' + mylist[index]['image'], mylist[index]['name'],
mylist[index]['name']));
//return new Specialties.fromMap(mylist[index]);

}


var jsonCodec = const JsonCodec();


}

Answer Source

You should use await when invoking your async methods. You can mark initState as async, it will still override.

Make sure to call setState() whenever you mutate member variables.

Check if (mounted) before setState if you are doing it after an async wait, because the widget may no longer be visible.

Consider using FutureBuilder instead of setState when doing async programming.