I am making a Flutter application with SQLite database in it with the sqflite package. I have a database helper class with the necessary methods. In one of the pages I want to display the data as a list of cards. I also have an image stored to the database, as such in the same page I have to convert the image back to a File. In the class of that page, named DataPage, I made a method called query which calls the query method of the database and assigns that value to a list called listLokasi. I also made a method called convert which calls List.generate with one of the arguments being listLokasi.length. Meanwhile I placed these 2 methods inside the constructor for _DataPageState as DataPage is a stateful widget. The problem is when I run the app an error displayed showing a NoSuchMethodError as I tried to call length on null, this means listLokasi is null. So I placed asserts in the query method, in the constructor after the query method, and in the convert method. The results is the assert in the query method did not fire, while the assert in the constructor immediately fired. I have inspected my database helper class and reviewed my code and I cannot find the flaw in my code. Any help in this problem would be appreciated. I shall display the code below.

This is the database helper class.

class DatabaseHelper {
  static final _instance = DatabaseHelper._internal();

  DatabaseHelper._internal();

  factory DatabaseHelper() {
    return _instance;
  }

  Database db;

  Future initDatabase() async {
    var databasePath = await getDatabasesPath();
    var path = join(databasePath, 'table.db');
    db = await openDatabase(path, version: 1, onCreate: onCreate);
  }

  onCreate(Database db, int version) async {
    await db.execute('''
    CREATE TABLE lokasi 
    (id INTEGER PRIMARY KEY, 
    name TEXT, 
    description TEXT, 
    category TEXT, 
    latitude REAL, 
    longitude REAL, 
    image BLOB)
    ''');
  }

  Future<Lokasi> insert(Lokasi lokasi) async {
    await db.insert('lokasi', lokasi.toJson());
    return lokasi;
  }

  Future<List<Lokasi>> query() async {
    var list = await db.query('lokasi');
    return List.generate(list.length, (i) => Lokasi.fromJson(list[i]));
  }
}

This is the DataPage class.

class DataPage extends StatefulWidget {
  final savedUsername;
  const DataPage({this.savedUsername = 'User'});

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

class _DataPageState extends State<DataPage> {
  DatabaseHelper db = DatabaseHelper();
  List<Lokasi> listLokasi;
  List<LokasiConvert> listLokasiConvert;

  _DataPageState() {
    query();
    assert(listLokasi != null);
    convert();
  }

  convert() {
    assert(listLokasi != null);
    listLokasiConvert = List.generate(
      listLokasi.length,
      (i) => LokasiConvert(
        name: listLokasi[i].name,
        description: listLokasi[i].category,
        category: listLokasi[i].category,
        latitude: listLokasi[i].latitude,
        longitude: listLokasi[i].longitude,
        image: File.fromRawPath(listLokasi[i].image),
      ),
    );
  }

  Future<List<Lokasi>> query() async {
    listLokasi = await db.query();
    assert(listLokasi != null);
    return listLokasi;
  }

  void sendUsername(BuildContext context) {
    String username = widget.savedUsername;
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => MainPage(username: username),
      ),
    );
  }

  void sendUsernameToChart(BuildContext context) {
    String chartUsername = widget.savedUsername;
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => ChartPage(savedUsername: chartUsername),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        automaticallyImplyLeading: false,
        title: Text('Data'),
        actions: [
          RaisedButton(
            child: Text('Logout'),
            onPressed: () {
              Navigator.popUntil(context, ModalRoute.withName('/'));
            },
          ),
          RaisedButton(
            child: Text('Main'),
            onPressed: () {
              sendUsername(context);
            },
          ),
          RaisedButton(
            child: Text('Charts'),
            onPressed: () {
              sendUsernameToChart(context);
            },
          ),
        ],
      ),
      body: ListView.builder(
        itemBuilder: (context, i) {
          return Card(
              child: Row(
            children: [
              Image.file(
                listLokasiConvert[i].image,
                width: 100,
                height: 100,
              ),
              Column(
                children: [
                  Text(listLokasiConvert[i].name),
                  Text(listLokasiConvert[i].category),
                  Text(listLokasiConvert[i].description),
                  Text('Coordinates: ' +
                      listLokasiConvert[i].latitude.toString() +
                      ', ' +
                      listLokasiConvert[i].longitude.toString()),
                ],
              )
            ],
          ));
        },
        itemCount: listLokasiConvert.length,
      ),
    );
  }
}

Once again, thank you for any help given.


Solution 1: Ananda Pramono

Maybe you can try this. Hope its help you.

db.query().then((value) {
  setState(() {
        listLokasi = value
  });
});


Solution 2: Ignas Prasetyo

The solution is actually simple as I discovered in my process. The list is a future so I am supposed to use a future builder and then wrap the listview builder with the future builder. Let me show my finished code of that specific page.

class DataPage extends StatefulWidget {
  final savedUsername;
  const DataPage({this.savedUsername = 'User'});

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

class _DataPageState extends State<DataPage> {
  DatabaseHelper db = DatabaseHelper();
  List<Lokasi> listLokasi;

  delete(value) async {
    await db.delete(value);
  }

  Future<List<Lokasi>> query() async {
    listLokasi = await db.query();
    return listLokasi;
  }

  void sendUsername(BuildContext context) {
    String username = widget.savedUsername;
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => MainPage(username: username),
      ),
    );
  }

  void sendUsernameToChart(BuildContext context) {
    String chartUsername = widget.savedUsername;
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => ChartPage(savedUsername: chartUsername),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        automaticallyImplyLeading: false,
        title: Text('Data'),
        actions: [
          RaisedButton(
            child: Text('Logout'),
            onPressed: () {
              Navigator.popUntil(context, ModalRoute.withName('/'));
            },
          ),
          RaisedButton(
            child: Text('Main'),
            onPressed: () {
              sendUsername(context);
            },
          ),
          RaisedButton(
            child: Text('Charts'),
            onPressed: () {
              sendUsernameToChart(context);
            },
          ),
        ],
      ),
      body: FutureBuilder(
        future: query(),
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return ListView.builder(
              itemBuilder: (context, i) {
                return Card(
                  child: Row(
                    children: [
                      Column(
                        children: [
                          Text(snapshot.data[i].name),
                          Text(snapshot.data[i].category),
                          Text(snapshot.data[i].description),
                          Text('Coordinates: ' +
                              snapshot.data[i].latitude.toString() +
                              ', ' +
                              snapshot.data[i].longitude.toString()),
                        ],
                      ),
                      Container(
                        width: 100,
                        height: 100,
                        child: Image.memory(snapshot.data[i].image),
                      ),
                      Container(
                        width: 30,
                        height: 30,
                        child: IconButton(
                          onPressed: () {
                            db.delete(snapshot.data[i].name);
                            setState(() {});
                          },
                          icon: Icon(Icons.delete, size: 30),
                          iconSize: 30,
                        ),
                      )
                    ],
                  ),
                );
              },
              itemCount: snapshot.data.length,
            );
          } else {
            return Center(child: CircularProgressIndicator());
          }
        },
      ),
    );
  }
}