Flutter problem with access to Sqflite data via FutureBuilder. ConnectionState.done, null

Issue

Trying to change access to database from:

simply initializing access to data initState and then showing results in ListView.builder

to

ListView.builder inside the FutureBuilder.

However I lost my way in this FutureBuilder and my list is empty. I guess my problem is inside updateAndGetList().
In console I am getting this:

flutter: AsyncSnapshot<List<Word>>(ConnectionState.done, null, , #0      _RandomWordsState.updateAndGetList.<anonymous closure> (package:myapplesson/main.dart:115:7)

Can somebody help or nudge me in a right direction?πŸ₯²

Here my main.dart

DatabaseHelper dbHelper = DatabaseHelper.instance;
final _suggestions = <Word>[];

  @override
  void initState() {
    super.initState();
    // initial load
    _listFuture = updateAndGetList();
  }

  late Future<List<Word>> _listFuture;

  void refreshList() {
    // reload
    setState(() {
      _listFuture = updateAndGetList();
    });
  }

//I GUESS I AM DOING SOMETHING WRONG HERE!?πŸ‘‡πŸ‘‡πŸ‘‡

  Future<List<Word>> updateAndGetList() async {
    await DatabaseHelper.instance.database;
    // return the list here
    return dbHelper.getAllWords().then((rows) {
      setState(() {
        rows?.forEach((row) { _suggestions.add(Word.map(row));});
      });
      throw '';
    });
  }

  Widget _buildSuggestions() {
    return FutureBuilder<List<Word>>(
      future: _listFuture,
      builder: (BuildContext context, AsyncSnapshot<List<Word>> snapshot) {
        if (snapshot.hasData) {
          return ListView.builder(
            itemCount: _suggestions.length,
            itemBuilder: (context, i) {
              if (i.isOdd) return const Divider();
              final index = i ~/ 2;
              if (index >= _suggestions.length) {
                _suggestions.addAll(_suggestions.take(10));
              }
              return _buildRow(_suggestions[index]);
            });
        }else if (snapshot.hasError) {
          return Text("Oops!");
        }
        return Center(child: CircularProgressIndicator());
      },
    );
  }

Widget _buildRow(Word pair) {
  return ListTile(
    title: Text(
      pair.word1
    ),
    subtitle: Text(
        pair.meaning
    ),
  );
}

Before the changes were done everything worked this way:

//perfectly workedπŸ˜€

@override
void initState() {
  super.initState();
  dbHelper.getAllWords().then((rows) {
    setState(() {
      rows?.forEach((row) { _suggestions.add(Word.map(row));});
    });
  });
}

Widget _buildSuggestions() {
  return ListView.builder(
      itemCount: _suggestions.length,
      itemBuilder: (context, i) {
        if (i.isOdd) return const Divider();
        final index = i ~/ 2;
        if (index >= _suggestions.length) {
          _suggestions.addAll(_suggestions.take(10));
        }
        return _buildRow(_suggestions[index]);
      });
}
Widget _buildRow(Word pair) {
  return ListTile(
    title: Text(
      pair.word1
    ),
    subtitle: Text(
        pair.meaning
    ),
  );
}

This is my Database_helper.dart

...
Future<List<Map<String, dynamic>>?> getAllWords() async {
    Database? db = await instance.database;
    var result = await db?.query(table);
    return result?.toList();
  }
...

Solution

In your updateAndGetList function, you are expected to return a Future<List<Word>>. Instead, you don’t return anything (but set the state instead). Consider adapting it to look a bit like the following:

  Future<List<Word>> updateAndGetList() async {
    await DatabaseHelper.instance.database;
    // return the list here
    return dbHelper.getAllWords().then((rows) {
      // build the list of words and then return it!
      List<Word> res = [];
      for (var row in rows) {
        res.add(Word.map(row));;      
      }
      return res;
    });
  }

You can now access this list of words from your snapshot in your future builder

return FutureBuilder<List<Word>>(
      future: _listFuture,
      builder: (BuildContext context, AsyncSnapshot<List<Word>> snapshot) {
        if (snapshot.hasData) {
          
          // the word list as retrieved from your future
          List<Word> wordList = snapshot.data;
          return ListView.builder(
            itemCount: wordList.length,
            itemBuilder: (context, i) {
              if (i.isOdd) return const Divider();
              final index = i ~/ 2;
              if (index >= wordList.length) {
                wordList.addAll(wordList.take(10));
              }
              // use the word listto build your rows
              return _buildRow(wordList[index]);
            });
        }else if (snapshot.hasError) {
          return Text("Oops!");
        }
        return Center(child: CircularProgressIndicator());
      },
    );

No need to keep a separate variable for your suggestions.

Moreover, you won’t really need your _listFuture variable, unless you require it somewhere else. In your FutureBuilder you can simply use

return FutureBuilder<List<Word>>(
  future: updateAndGetList(),
  builder: ...
);

Answered By – fravolt

Answer Checked By – Jay B. (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.