1

I have a stateful widget whose state builds a ListView. The ListView gets its data from an http API. I am using a Future<void> method called getData to retrieve this data and populate a List<> with it before calling setState.

My question is where should I call getData when this screen first launches? If I call it in initState(), I get the following error in the debug console:

[VERBOSE-2:ui_dart_state.cc(198)] Unhandled Exception: dependOnInheritedWidgetOfExactType<_InheritedTheme>() or dependOnInheritedElement() was called before _EventListState.initState() completed.

If I wrap the call to getData in a delayed Future, I do not see the error. Here's my code:

class _EventListState extends State<EventList> {

  Future<void> getData() async {
    events = [];
    events = await Network.getUsers(context);
    setState(() {});
  }

  List<Event> events = [];

  @override
  initState() {
    super.initState();
    getData(); // this cause the error
    // Future.delayed(Duration(seconds: 1), getData); // this works
  }

  @override
  build(context) {
    return PlatformScaffold(
      iosContentPadding: true,
      body: ListView.builder(
        padding: const EdgeInsets.all(10),
        physics: const AlwaysScrollableScrollPhysics(),
        itemCount: events.length,
        itemBuilder: (context, index) => Text(events[index].summary),
      ),
    );
  }
}

Forcing a delay to retrieve the data does not feel right, so is there a better way?

barry
  • 4,037
  • 6
  • 41
  • 68

1 Answers1

2

Use FutureBuilder.

List<Event> events = [];

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          return PlatformScaffold(
            iosContentPadding: true,
            body: ListView.builder(
              padding: const EdgeInsets.all(10),
              physics: const AlwaysScrollableScrollPhysics(),
              itemCount: events.length,
              itemBuilder: (context, index) => Text(events[index].summary),
            ),
          );
        } else if (snapshot.hasError) {
          return Center(child: Text('Error: ${snapshot.error}'));
        } else {
          return Center(child: Text('Please wait its loading...'));
        }
      },
      future: getData(),
    );
  }

  Future<void> getData() async {
    events = [];
    events = await Network.getUsers(context);
  }
  • Thank you Divya. I am now trying to use a RefreshIndicator alongside FutureBuilder and the UI is not updating - will post a separate question. – barry Aug 15 '22 at 13:53