0

我正在 Flutter 中编写一种待办事项列表,并启用了 null 安全性。我使用 sqflite 将待办事项列表存储在 sqlite db 上。

项目类如下:

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 的类,它充当 Item 类和 db_controller 类之间的中介。这是 items_controller 类:

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 方法)我想使用从数据库中查询的项目列表的长度作为索引来构建一个列表。但我有这个错误(如标题)[![screenshot][1]][1] [1]: https://i.stack.imgur.com/Z6VAh.png

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

编辑 我已经实现了 FutureBuilder,但我对 .length 属性没有运气:[![snapshot][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.
4

3 回答 3

2

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

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

于 2021-09-25T13:52:03.123 回答
1

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

于 2021-09-25T13:57:49.667 回答
0

你猜对了,因为它是未来,所以你必须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

重要的提示

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

于 2021-09-25T13:51:14.007 回答