1
votes

I'm trying to fill a ListView with account information, which gets fetched from a Sqflite database. Here is how I read the account data from db:

dbhelper.dart

  Future<List<AccountModel>> getAllAccounts() async {
    final dbClient = await db;
    print('getAllAccounts');
    List<Map> res = await dbClient.rawQuery('SELECT * FROM Accounts');
    if (res.isNotEmpty) {
      print('Accounts $res');
      var accounts = [];
      for (var i = 0; i < res.length; i++) {
        var account =AccountModel.fromDb(res[i]);
        print('Account $account');
        accounts.add(account);
      }
      print('Return $accounts');
      return accounts;
    }
    return [];

This is, how I try to contruct the list view, using the FutureBuilder-Widget:

account_list_widget.dart

class AccountList extends StatefulWidget {
  State<StatefulWidget> createState() {
    return AccountListState();
  }
}

class AccountListState extends State<AccountList> {
  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        FutureBuilder<List<AccountModel>>(
          future: DbProvider().getAllAccounts(),
          builder: (BuildContext context, AsyncSnapshot<List<AccountModel>> snapshot) {
            if (snapshot.hasData) {
              return  Stack(
                children: <Widget>[
                  ListView.builder(
                    itemCount: snapshot.data.length + 1,
                    itemBuilder: (BuildContext context, int index) {
                      if (index == 0) {
                        return ListSectionHeader('Konten');
                      } else {
                        // Korrigieren des Index, da ja die 0 schon "verbraucht" wurde,
                        // auf die Daten aber weiterhhin mit Start-Index 0 zugegriffen wird.
                        index--;
                        AccountModel account = snapshot.data[index];
                        return Dismissible(
                          key: UniqueKey(),
                          background: Container(color: Colors.red),
                          onDismissed: (direction) {
                            DbProvider().removeAccount(account.uuid);
                          },
                          child: ListTile(
                            title: Text(account.accountName),
                            leading: Icon(Icons.crop_square),
                            trailing: Icon(Icons.keyboard_arrow_right),
                          ),
                        );
                      }
                    },
                  ),
                  addAccountTile()
                ],
              );
            } else {
              return ListView.builder(
                itemCount: 2,
                itemBuilder: (BuildContext context, int index) {
                  if (index == 0) {
                    return ListSectionHeader('Konten2');
                  } else {
                    return addAccountTile();
                  }
                },
              );
            }
          },
        ),
      ],
    );
  }

  Widget addAccountTile() {
    return ListTile(
      title: Text('Neues Konto hinzufügen'),
      leading: Icon(
        Icons.add_circle,
        color: Colors.blue,
      ),
      trailing: Icon(Icons.keyboard_arrow_right),
      onTap: () {
        Navigator.pushNamed(context, '/newaccount');
      }
    );
  }
}

The custom data model is defined like this:

account_model.dart

class AccountModel {
  final String uuid;
  final String accountName;
  final String host;
  final String userName;
  final String password;
  final String lastConnected;
  final bool active;
  final bool useHeadset;

  AccountModel(this.uuid, this.accountName, this.host, this.userName, 
    this.password, this.lastConnected, this.active, this.useHeadset);

  AccountModel.fromJson(Map<String, dynamic> parsedJson)
    : uuid = parsedJson['uuid'],
      accountName = parsedJson['accountName'],
      host = parsedJson['host'],
      userName = parsedJson['userName'],
      password = parsedJson['password'],
      lastConnected = parsedJson['lastConnected'],
      active = parsedJson['active'],
      useHeadset = parsedJson['useHeadset'];

  AccountModel.fromDb(Map<String, dynamic> parsedJson) 
    : uuid = parsedJson['uuid'], 
    accountName = parsedJson['accountName'],
    host = parsedJson['host'],
    userName = parsedJson['userName'],
    password = parsedJson['password'],
    lastConnected = parsedJson['lastConnected'],
    active = parsedJson['active'] == 1,
    useHeadset = parsedJson['useHeadset'] == 1;

  Map<String, dynamic> toMap() {
    return <String, dynamic> {
      'uuid': uuid,
      'accountName': accountName,
      'host': host,
      'userName': userName,
      'password': password,
      'lastConnected': lastConnected,
      'active': active ? 1 : 0,
      'useHeadset': useHeadset ? 1 : 0
    };
  }
}

My problem is, that although the database stores valid data and also the method getAllAccounts()seems to return valid data, the statement snapshot.hasdatainside the FutureBuilder widget alsways is false.

What goes wrong here?

From the console:

D/Sqflite ( 9179): [Thread[main,5,main]] opened 6 /data/user/0/com.example.dashboard/app_flutter/test.db total open count (6)
D/Sqflite ( 9179): [Thread[Sqflite,5,main]] BEGIN EXCLUSIVE
D/Sqflite ( 9179): [Thread[Sqflite,5,main]] PRAGMA user_version;
D/Sqflite ( 9179): [Thread[Sqflite,5,main]] PRAGMA user_version = 1;
D/Sqflite ( 9179): [Thread[Sqflite,5,main]] COMMIT
I/flutter ( 9179): getAllAccounts
D/Sqflite ( 9179): [Thread[Sqflite,5,main]] SELECT * FROM Accounts
I/flutter ( 9179): Accounts [{uuid: 436f2c10-3260-11e9-fe03-65fdfe3a7bbd, accountName: 12345, host: 12345, userName: 12345, password: 12345, lastConnected: 0, active: 0, useHeadset: 0}]
I/flutter ( 9179): Account Instance of 'AccountModel'
I/flutter ( 9179): Return [Instance of 'AccountModel']

(The last line of this snippet from the console corresponds with the print statement at the end of the "getAllAccounts" method.)

1
add print('snapshot: $snapshot'); before if (snapshot.hasData) {, what do you see?pskink
It prints to snapshot: AsyncSnapshot<List<AccountModel>>(ConnectionState.done, [], null)rweinbrecher
and with print('snapshot data: ${snapshot.data}');? is it: snapshot data: []?pskink
Yes. Exactly. This prints: snapshot data: []rweinbrecher
so your getAllAccounts() method returned empty list (return [];) but still snapshot.hasData should be truepskink

1 Answers

0
votes

I solved the problem:

In the database provider in the method "getAllAccounts() I had to explicitly declare the type of the accounts object like so:

List<AccountModel> accounts = [];