“Future<List>”类型的值不能分配给“List”类型的变量

时间:2021-06-14 02:14:52

标签: flutter sqlite casting future

我真的是 Flutter 和 SQLite 的新手。 我需要将从数据库中获取的一些数据存储到全局变量中(在此代码中,它是一个局部变量,仅用于举例),但我不知道:

  1. 我能做到的最佳点在哪里(现在我把它放在主页的 initState 方法中);
  2. 我如何将未来数据存储在没有未来的变量中。

以下是数据提取的方法

class ObjectRepository {
  ObjectRepository._(); 
  static final ObjectRepository instance = ObjectRepository._();

  Future<List<Map>> select(Database db, String tableName, {List<String> fields}) async {
        sql = 'SELECT ' + (fields != null ? fields.toString() : '* ');
        sql = sql + 'FROM $tableName';
    
        final List<Map> list = await db.rawQuery(sql);
    
        return list;
  }
}

下面是我将我的 future> 放入 List 的地方(我知道我不能,事实上 IDE 给了我这个错误 A 类型的值 'Future>>' 不能分配给类型为 'List>' 的变量。尝试更改变量的类型,或将右侧类型转换为 'List<地图<动态,动态>>'

我什至尝试投射它,但我在运行时遇到了类似的问题"type 'Future>>' is not a subtype of type 'List>'在类型转换中”

class HomePage extends StatefulWidget {
  HomePage({Key key}) : super(key: key);

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

class _HomePageState extends State<HomePage> {
  @override
  void initState() {
    super.initState();

    DBProvider.instance.openDB(globalConstants.dbName);
    List<Map> list = ObjectRepository.instance.select(DBProvider.instance.database, 'tCars') as List<Map>;
  }

  @override
  void dispose() {
    DBProvider.instance.closeDB();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: CustomAppBar(title: Text('Cars')),
      drawer: CustomDrawer(),
      body: CustomBody(),
    );
  }
}

知道如何解决吗?非常感谢,伙计们。

2 个答案:

答案 0 :(得分:1)

从数据库读取是一种异步活动,这意味着查询不会立即返回一些数据。所以你必须等待操作完成,然后将其赋值给一个变量。

DBProvider.instance.openDB(globalConstants.dbName);
List<Map> list = await ObjectRepository.instance.select(DBProvider.instance.database, 'tCars') as List<Map>;

另一方面,您不能在 await 中使用 initState。 解决方案是创建一个 async 函数来处理从数据库中获取数据的过程。

Future<List<Map>> initDB()async{
  DBProvider.instance.openDB(globalConstants.dbName);
  List<Map> list = await ObjectRepository.instance.select(DBProvider.instance.database, 'tCars') as List<Map>;
  return list;
}

终于可以在initDB内调用initState

答案 1 :(得分:0)

错误是因为 instant.select 函数返回 Future<List<Map>>,但您试图将其分配给 List<Map>,因此您必须等待 future 完成。但 initState 不是 async 函数。因此你不能在那里await。但是您可以在 future 上使用 then 方法(当 future 完成时调用该值)。并将 callback (您要使用该值执行的操作)赋予 then 方法。但是在使用 list 语句之前,您必须仔细检查 if 是否为 null,因为它最初设置为 null。

    class HomePage extends StatefulWidget {
    
      HomePage({Key key}) : super(key: key);
    
      List<Map>? list;
      @override
      _HomePageState createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
      @override
      void initState() {
        super.initState();
    
        DBProvider.instance.openDB(globalConstants.dbName);
        ObjectRepository.instance.select(DBProvider.instance.database, 'tCars').then((result){
           if(mounted) {
             setState((){ list = result});
              }else { list = result}
            });
      }
    
      @override
      void dispose() {
        DBProvider.instance.closeDB();
    
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: CustomAppBar(title: Text('Cars')),
          drawer: CustomDrawer(),
          body: CustomBody(),
        );
      }
    }

如果您想将您的列表用于小部件(例如:ListView),则可以立即使用。考虑使用 FutureBuilder ,它需要 futurebuilder 函数根据您给它的未来状态返回适当的 widget。例如,

 FutureBuilder<List<Map>>(
   future: ObjectRepository.instance.select(DBProvider.instance.database, 'tCars') // your future here,
builder : (context, snapshot) {
       if (snapshot.hasError) return ErorrScreen();
       if (snapshot.hasData) return ListView.builder(itemCount :  snapshot.data.length,
         itemBuilder: (context, index) {
             return Text(snpshot.data[index].toString());
});
return CicularProgressIndicator();
}
)

这是在 Flutter 中更推荐在 UI 中使用期货的方式。