Home > Blockchain >  The getter 'length' isn't defined for the type 'Future<List<Item>>&#
The getter 'length' isn't defined for the type 'Future<List<Item>>&#

Time:09-26

I'm writing a kind of todo list in Flutter, with null safety enabled. I'm storing the list of todo on a sqlite db using sqflite.

The Item class is the following:

class Item {
  int? _id;
  String? _name;
  String? _notes;
  String? _expiryDate;
  int? _category;
  int? _quantity;
  
  Item(
    this._id,
    this._name,
    this._notes,
    this._expiryDate,
    this._category,
    this._quantity,
  );

  // names for db table and columns
  String tableItems = 'items';
  String colId = 'id';
  String colName = 'name';
  String colNotes = 'notes';
  String colExpiryDate = 'expiryDate';
  String colCategory = 'category';
  String colQuantity = 'quantity';
  
  // Getters
  int? get id => _id;
  String? get name => _name;
  String? get notes => _notes;
  String? get expiryDate => _expiryDate;
  int? get category => _category;
  int? get quantity => _quantity;

  // Imports the Item details from a map
  Item.fromMap(Map map) {
    this._id = map[colId];
    this._name = map[colName];
    this._notes = map[colNotes];
    this._expiryDate = map[colExpiryDate];
    this._category = map[colCategory];
    this._quantity = map[colQuantity];
  }

  // Converts a Item into a Map
  Map<String, dynamic> toMap() {
    var map = Map<String, dynamic>();
    map['id'] = _id;
    map['name'] = _name;
    map['notes'] = _notes;
    map['expiryDate'] = _expiryDate;
    map['category'] = _category;
    map['quantity'] = _quantity;
    return map;
  }

  // Implement toString to make it easier to see information about
  // each dog when using the print statement.
  @override
  String toString() {
    return 'Item{id: $_id, name: $_name, notes: $_notes, expiryDate: $_expiryDate, category: $_category, quantity: $_quantity}';
  }
  
}

I have a class named items_controller which works as an intermediary between the Item class and the db_controller class. This is the items_controller class:

import 'package:get/get.dart';
import 'package:<redacted>/model/item.dart';

import 'db_controller.dart';

class ItemsController extends GetxController {
  // names for db table and columns
  String tableItems = 'items';
  String colId = 'id';
  String colName = 'name';
  String colNotes = 'notes';
  String colExpiryDate = 'expiryDate';
  String colCategory = 'category';
  String colQuantity = 'quantity';

  //TODO
  void loadItem() {}

  // function to retrieve all the items from the db
  Future<List<Item>> loadAllItems() async {
    final dbController = DBController();
    final List<Map<String, dynamic>> itemsMaps =
        await dbController.query(tableItems);

    return List.generate(itemsMaps.length, (i) {
      return Item.fromMap(itemsMaps[i]);
    });
  }

  // function to add an item to the db
  Future<int> addItem(Item item) async {
    final dbController = DBController();
    // add a new Item to the table and get the id
    int id = await dbController.insertElement(tableItems, item);
    return id;
  }

  //TODO
  void updateItem() {}

  //TODO
  void deleteItem(int index) {}
}

Given that, from the list_builder (which uses the ListView.builder method) I want to build a list using as index the length of the list of Items queried from the DB. But I have this error (as in title) [![screenshot][1]][1] [1]: https://i.stack.imgur.com/Z6VAh.png

I'm quite sure that the Future<List> that I'm getting when calling loadAllItems() should have a length property because it's a list, but I'm not sure, maybe because it's a Future List? Help please!

EDIT I've implemented the FutureBuilder, but I had no luck with the .length attribute: [![snapshot][2]][2] [2]: https://i.stack.imgur.com/QswA0.png

If I run anyway the code, I get the following errors:

Performing hot restart...
Syncing files to device AC2003...
lib/views/list_builder.dart:21:45: Error: The getter 'length' isn't defined for the class 'Object'.
 - 'Object' is from 'dart:core'.
Try correcting the name to the name of an existing getter, or defining a getter or field named 'length'.
                  itemCount: snapshot.data!.length,
                                            ^^^^^^
lib/views/list_builder.dart:16:20: Error: A non-null value must be returned since the return type 'Widget' doesn't allow null.
 - 'Widget' is from 'package:flutter/src/widgets/framework.dart' ('../../flutter/packages/flutter/lib/src/widgets/framework.dart').
          builder: (context, snapshot) {
                   ^
Restarted application in 671ms.

CodePudding user response:

Since the loadAllItems() call is a Future, you will need to wrap your ListBuilder in a FutureBuilder. Watch this video for some more information.

Basically, you need to await the results of the Future<List> so that it becomes a List.

CodePudding user response:

FutureBuilder is what you required to load async data, and get list when the ConnectionState is done

FutureBuilder(
          future: controller.loadAllItems(),
          builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
            if (snapshot.connectionState == ConnectionState.done) {
              int count =  snapshot.data.length; // use this as itemCount in Listview
              return ListView.Builder(); // write builder code here
            }
          }),

For read https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

CodePudding user response:

you guessed it right, because it's a future, so you have to use await before the function,

you can use a FutureBuilder and pass the function to the future parameter, and access the list through the snapshot, something like that:

GetX<ItemController>(
              builder: (controller) => FutureBuilder<List<Item>>(
                future: controller.loadAllItems(),
                builder: (context, snapshot) => snapshot.connectionState == ConnectionState.waiting
                    ? CircularProgressIndicator()
                    : snapshot.hasData
                        ? ListView.builder(
                            itemCount: snapshot.data.length,
                            itemBuilder: (context, index) => Container(
                                //data widget
                                ),
                          )
                        : Container(
                            // empty widget
                            ),
              ),
            ),

or you can the call the function when the page loads, and save the items in variable (in controller maybe, or the widget itself), and then use that variable to get the length

important note

make sure that future builder return a widget in every case, in your updated question, there is an error which indicates that the builder returns a null

  • Related