0

I have a screen that is reading data from a database table (sqllite) but the data is not been display in my listview. it looks like when i read the data the screen already display the gui and my array is not populated with data by the time the listview access the array. below is my code

import 'package:finsec/model/cardview_list_item.dart';
import 'package:flutter/material.dart';
import 'package:finsec/utils/strings.dart';
import 'package:finsec/utils/colors.dart';
import 'package:finsec/widget/circle_icon.dart';
import 'package:finsec/data/db_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:finsec/utils/queries.dart';

class CardviewListItemData  extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {

    return CardviewListItemDataState();
  }
}

class CardviewListItemDataState extends State<CardviewListItemData> {
  DBProvider db = new DBProvider();
  List<String> amountList;
  List<CardviewListItem> listItems  = List<CardviewListItem>();
  int count = 0;

  @override
  Widget build(BuildContext context) {
    if (amountList == null) {
      amountList = List<String>();

      updateListView();
      listItems = summaryList();

    }

    return ListView.builder(
      itemCount: listItems.length,
      shrinkWrap: true,
      itemBuilder: (context, index) {
        return ListTile(
          leading: Icon(listItems[index].icon, color: listItems[index].iconColor),
          title: Align(
            child: new Text(listItems[index].title,style: TextStyle(fontSize: 16)),
            alignment: Alignment(-1.4, 0),
          ),
          trailing: new Text(listItems[index].amount, style: TextStyle(fontSize: 16),),
          //onTap: () => onTapped(context, listItems[index].title),
        );
      },
    );
  }
  List<CardviewListItem> summaryList() {


    List<CardviewListItem> summaryListItems = List<CardviewListItem>();
    CardviewListItem income = new CardviewListItem(
        title: totalIncome,
        amount: amountList[0],
        icon: Icons.attach_money,
        iconColor: green

    );
    summaryListItems.add(income);


    return summaryListItems;
  }



  void updateListView() {
    final Future<Database> dbFuture = db.initializeDatabase();
    dbFuture.then((database) {

      Future<List<String>> incomeListFuture = db.getDataList(test);
      incomeListFuture.then((amountList) {
        setState(() {
          this.amountList = amountList;
          this.count = this.amountList.length;
        });
      });
    });
  }


}

my database code is

import 'dart:io';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:finsec/model/transaction_content.dart';
import 'package:finsec/utils/tables.dart';
import 'package:finsec/model/income/income_dao.dart';
import 'package:finsec/model/income/income.dart';
import 'package:finsec/model/list_item.dart';
import 'package:finsec/model/dao.dart';
import 'package:finsec/model/cardview_list_item.dart';

import 'package:finsec/utils/strings.dart';
import 'package:finsec/utils/colors.dart';
import 'package:finsec/widget/circle_icon.dart';
import 'package:finsec/data/db_provider.dart';
class DBProvider {

  static DBProvider db;    // Singleton DBProvider
  static Database _database;                // Singleton Database

  DBProvider._createInstance(); // Named constructor to create instance of DBProvider

  factory DBProvider() {

    if (db == null) {
      db = DBProvider._createInstance(); // This is executed only once, singleton object
    }
    return db;
  }

  Future<Database> get database async {

    if (_database == null) {
      _database = await initializeDatabase();
    }
    return _database;
  }

  Future<Database> initializeDatabase() async {
    // Get the directory path for both Android and iOS to store database.
    Directory directory = await getApplicationDocumentsDirectory();
    String path = directory.path + 'finsec.db';
    // Open/create the database at a given path
    var finsecDatabase = await openDatabase(path, version: 4, onCreate: _createDb, onUpgrade: _onUpgrade);
    return finsecDatabase;
  }

  void _createDb(Database db, int newVersion) async {
   // await db.execute('drop table income');
    print('CREATE THE TABLE2');
    await db.execute(incomeTable);

    //await db.execute("CREATE TABLE User(id INTEGER PRIMARY KEY, firstname TEXT, lastname TEXT, dob TEXT)");
  }

  // UPGRADE DATABASE TABLES
  void _onUpgrade(Database db, int oldVersion, int newVersion) async {
    if (oldVersion < newVersion) {
      await db.execute('drop table income');
      await db.execute(incomeTable);
    }
  }

  // Fetch Operation: Get all row objects from database
  Future<List<Map<String, dynamic>>> getTransactions(String tableName) async {
    Database db = await this.database;

   //var result = await db.rawQuery(tableName);
    var result = await db.query(tableName);
    result.forEach((row) => print(row));
   // var res = await db.insert('note', transactionType.toMap());
    return result;
  }

  // Fetch Operation: Get all row objects from database
  Future<List<Map<String, dynamic>>> getTransactionsQuery(String query) async {
    Database db = await this.database;

    var result = await db.rawQuery(query);
    return result;
  }


  // Insert Operation: Insert a transaction object to database
  Future<int> insertTransaction(String sqlQuery) async {
    Database db = await this.database;
    //var dao = new IncomeDao();
    var result = await db.rawInsert(sqlQuery); //insert(noteTable, note.toMap());
    //var result = await db.insert('noteTable', transaction);
    return result;
  }

  // Update Operation: Update a Note object and save it to database
  Future<int> updateTransaction(Map transaction) async {
    var db = await this.database;
    //var result = await db.rawUpdate(sql);// update(noteTable, note.toMap(), where: '$colId = ?', whereArgs: [note.id]);
    var result = await db.update('noteTable', transaction);
    return result;
  }

  // Delete Operation: Delete a transaction object from database
  Future<int> deleteTransaction(int id) async {
    var db = await this.database;
    int result = await db.rawDelete('DELETE FROM noteTable WHERE ');
    return result;
  }

  // Get number of Note objects in database
  Future<int> getCount() async {
    Database db = await this.database;
    List<Map<String, dynamic>> x = await db.rawQuery('SELECT COUNT (*) from noteTable');
    int result = Sqflite.firstIntValue(x);
    return result;
  }

  // Get the 'Map List' [ List<Map> ] and convert it to 'Note List' [ List<Note> ]
  Future<List<Income>> getIncomeList(String tableName) async {

    var incomeMapList = await getTransactions(tableName); // Get 'Map List' from database
    int count = incomeMapList.length;         // Count the number of map entries in db table

    List<Income> incomeList = List<Income>();
    // For loop to create a 'Income List' from a 'Map List'
    for (int i = 0; i < count; i++) {
      incomeList.add(Income.fromMap(incomeMapList[i]));
    }

    return incomeList;
  }

  // Get the 'Map List' [ List<Map> ] and convert it to 'Note List' [ List<Note> ]
  Future<List<String>> getDataList(String query) async {
    var dataMapList = await getTransactionsQuery(query); // Get 'Map List' from database
    int count = dataMapList.length;         // Count the number of map entries in db table

    List<String> dataList = List<String>();
    Map<String, dynamic> queryData;



    for (int i = 0; i < count; i++) {
      queryData = dataMapList[i];
      dataList.add(queryData["amount"]);
    }

    return dataList;
  }

}

i think the problem is in the following portion of the code

  if (amountList == null) {
      amountList = List<String>();
      updateListView();
      listItems = summaryList();
    }

updateListView() is getting called but it looks like the code continue executing to the next line even if updateListView didnt finish executing. when i called amountList[0] in summaryList(), i get the following error

 RangeError (index): Invalid value: Valid value range is empty: 0
I/flutter ( 3640): 
I/flutter ( 3640): User-created ancestor of the error-causing widget was:
I/flutter ( 3640):   Column file:///C:/Users/rodrigue33/Documents/APP/finsec/lib/widget/cardview_widget.dart:45:22
I/flutter ( 3640): 
I/flutter ( 3640): When the exception was thrown, this was the stack:
I/flutter ( 3640): #0      List.[] (dart:core-patch/growable_array.dart:145:60)
I/flutter ( 3640): #1      CardviewListItemDataState.summaryList (package:finsec/data/cardview_list_item.dart:114:27)
I/flutter ( 3640): #2      CardviewListItemDataState.build (package:finsec/data/cardview_list_item.dart:80:19)
I/flutter ( 3640): #3      StatefulElement.build (package:flutter/src/widgets/framework.dart:4040:27)

this is my assumption of what is happening. do anyone agree with me? how can i solve this issue of waiting for database to return data before displaying the data in the listview? thanks in advance

1 Answer 1

1

The problem is that you are calling the setState method and updating its "amountList", as setState reloads the page it doesn't quite execute "listItems = summaryList ();".

Perhaps a quick fix would be to create a bool check and leave it false, set to true when setState is executed, and within your builder method do:

if (amountList == null) {
  amountList = List<String>();
  updateListView();
}
 if(check){ 
   listItems = summaryList();
 }

NOTE: I suggest using async and await in your methods instead of future.

void updateListView() async{

  final Database db = await db.initializeDatabase();
  List<String> incomeList = await db.getDataList(test);
  setState(() {
    this.amountList = amountList;
    this.count = this.amountList.length;
    this.check = true;
  });


}

I hope I helped you!

Sign up to request clarification or add additional context in comments.

3 Comments

wow, this works great thanks. quick question, why use async and await instead of future? is there any benefits? does this mean i also have to change my database class not to use future? please explain why use async and await instead of future?
The use depends on your use case, and goes your preference as well. When using async and await you are informing the flutter that it must wait for that item to finish executing to follow the execution, while with future it continues executing the other methods. I don't know if I was very clear! But I suggest you take a read here: dart.dev/codelabs/async-await
I would appreciate if you mark the answer and vote for it, so that more people facing the problem find a possible solution!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.