getter 'length' 没有为类型 'Future<List<Item>>' 定义

The getter 'length' isn't defined for the type 'Future<List<Item>>'

我正在 Flutter 中编写一种待办事项列表,启用了空安全。 我正在使用 sqflite 将待办事项列表存储在 sqlite 数据库中。

项目 class 如下:

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}';
  }
  
}

我有一个名为 items_controller 的 class,它充当项目 class 和 db_controller class 之间的中介。 这是 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) {}
}

鉴于此,从 list_builder(使用 ListView.builder 方法)我想构建一个列表,使用从数据库查询的项目列表的长度作为索引。 但是我有这个错误(如标题所示) [![截图][1]][1] [1]: https://i.stack.imgur.com/Z6VAh.png

我很确定我在调用 loadAllItems() 时得到的 Future 应该有一个 length 属性 因为它是一个列表,但我不确定,也许是因为它是 Future List? 请帮忙!

编辑 我已经实现了 FutureBuilder,但是我没有使用 .length 属性: [![快照][2]][2] [2]: https://i.stack.imgur.com/QswA0.png

如果我 运行 代码,我会得到以下错误:

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.

你猜对了,因为是future,所以函数前要用await

你可以使用 FutureBuilder 并将函数传递给 future 参数,并通过快照访问列表,类似这样:

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
                            ),
              ),
            ),

或者您可以在页面加载时调用该函数,并将项目保存在变量中(可能在控制器中,或者小部件本身),然后使用该变量获取 length

重要说明

确保未来的构建器 return 在每种情况下都是一个小部件,在您更新的问题中,有一个错误表明构建器 return 是空的

由于 loadAllItems() 调用是一个 Future,因此您需要将 ListBuilder 包装在 FutureBuilder 中。观看此视频了解更多信息。

基本上,您需要等待 Future 的结果,以便它成为一个列表。

FutureBuilder 是加载异步数据所需的,并在 ConnectionState 完成后获取列表

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
            }
          }),

待读https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html