0

I am new to Flutter and building a small app to record my expenses and learn a bit.

I am using Hive to store data. Now I am building a page which targets to show all the previously saved entries. I do this by creating a List with all the data and then trying to use a FutureBuilder to show the data in a ListView.

This is the code so far:


class LogScreen extends StatefulWidget {
  const LogScreen({Key? key}) : super(key: key);

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

class _LogScreenState extends State<LogScreen> {
  get futureEntries => getEntries();

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<Widget>(
        future: futureEntries,
        builder: (BuildContext context, AsyncSnapshot<Widget> snapshot) {
          if (snapshot.hasData) {
            return Container(
                child: ListView.builder(
                  itemCount: futureEntries.length,
                  itemBuilder: (context, index) {
                    Entry currentEntry = Hive.box<Entry>('entriesBox').getAt(index);
                    return ListTile(
                      title: Text('${currentEntry.description}'),
                    );
                  },
                ),
            );
          } else {
              return CircularProgressIndicator();
          }
        }
    );
  }

  Future<List> getEntries() async {
    List listEntries = await DbHelper().getListEntries();
    print(listEntries);
    return listEntries;
  }

}

I am getting the following error though:

The following _TypeError was thrown building LogScreen(dirty, state: _LogScreenState#75644):
type 'Future<List<dynamic>>' is not a subtype of type 'Future<Widget>?'

The relevant error-causing widget was: 
  LogScreen file:///home/javier/StudioProjects/finanzas/lib/main.dart:55:14
When the exception was thrown, this was the stack: 
#0      _LogScreenState.build (package:finanzas/log_screen.dart:29:17)

Could someone please tell me what I am doing wrong and suggest a solution? I come from Python and am having a though time with all these types :-P

Thanks in advance.

padaleiana
  • 955
  • 1
  • 14
  • 23

2 Answers2

0

The generic type of FutureBuilder<T>() should correspond to the data type your Future will return, not what the builder is building. In your case you have FutureBuilder<Widget> so it expects a Future<Widget>, but your getEntries returns a Future<List<dynamic>>. So this is what the error is hinting at. Your code should probably look like this:

return FutureBuilder<List<Entry>>(
        future: futureEntries,
        builder: (BuildContext context, AsyncSnapshot<List<Entry>> snapshot) {
          if (snapshot.hasData) {
            return Container(
                child: ListView.builder(
                  itemCount: snapshot.data.length,
                  itemBuilder: (context, index) {
                    Entry currentEntry = snapshot.data[index];
                    return ListTile(
                      title: Text('${currentEntry.description}'),
                    );
                  },
                ),
            );
          } else {
              return CircularProgressIndicator();
          }
        }
    );

Also note that i replaced the references in your ListView.builder from directly referencing your future to using the data inside the snapshot

puelo
  • 5,464
  • 2
  • 34
  • 62
  • Thank you very much for your response. That could absolutely be it. I corrected it and now I get a error at the line: itemCount: snapshot.data.length, The IDE tells me snapshot.data could be null and I should regard that case. Shouldn't that branch only be executed in case that snapshot has some data? – Javier Malonda Jun 24 '21 at 19:14
  • Yes, but the null saftey can not determine that because you check `hasData` above that this will be non-null. You can just do `snapshot.data!.length` – puelo Jun 24 '21 at 20:40
0

Alright. After some research, here's the code that got to work:

  Widget build(BuildContext context) {
    return FutureBuilder<List>(
        future: futureEntries,
        builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
          if (snapshot.hasData) {
            return Container(
                child: ListView.builder(
                  itemCount: snapshot.data!.length,
                  itemBuilder: (context, index) {
                    Entry currentEntry = snapshot.data![index];
                    return ListTile(
                      title: Text('${currentEntry.description}'),
                    );
                  },
                ),
            );
          } else {
              return CircularProgressIndicator();
          }
        }
    );
  }

  Future<List> getEntries() async {
    List listEntries = await DbHelper().getListEntries();
    print(listEntries);
    return listEntries;
  }

I don't know yet exactly what the exclamation marks after 'data' do, but they did the trick.