Flutter使用sqflite查询SQLite赋值列表为空调用length报错如何解决
问题根因
你遇到的报错本质是异步方法执行时序问题:
query()是异步方法,调用后不会等待数据库查询完成就会直接执行后续的assert和convert()方法,此时listLokasi还没被赋值,自然为null。- 构造函数不支持
async/await,没法等待异步操作完成再执行后续逻辑。 - 另外你还有两个隐藏问题:数据库实例没有主动初始化、BLOB转File的方法使用错误。
修复步骤
1. 调整初始化逻辑
将异步初始化逻辑移到State的initState生命周期方法中执行,查询、转换完成后调用setState更新页面状态:
class _DataPageState extends State<DataPage> { DatabaseHelper db = DatabaseHelper(); List<Lokasi> listLokasi = []; List<LokasiConvert> listLokasiConvert = []; // 增加loading状态控制 bool isLoading = true; @override void initState() { super.initState(); initData(); } Future<void> initData() async { // 先初始化数据库,必须调用否则db实例为null await db.initDatabase(); await query(); await convert(); if (mounted) { setState(() { isLoading = false; }); } } // 其余原有方法保持 }
2. 修复BLOB转File逻辑
File.fromRawPath是把传入的字节当做文件路径解析,不能直接用来把图片字节转成File,需要先把BLOB返回的Uint8List写入本地临时文件再生成File对象。
需要先在pubspec.yaml中引入官方路径工具包:
dependencies: flutter: sdk: flutter path_provider: ^2.1.0
然后修改convert方法:
Future<void> convert() async { final tempDir = await getTemporaryDirectory(); listLokasiConvert = List.generate( listLokasi.length, (i) { // 生成临时文件 final tempFile = File('${tempDir.path}/${listLokasi[i].id}.png'); tempFile.writeAsBytesSync(listLokasi[i].image); return LokasiConvert( name: listLokasi[i].name, description: listLokasi[i].description, // 你原有代码这里写错成category了 category: listLokasi[i].category, latitude: listLokasi[i].latitude, longitude: listLokasi[i].longitude, image: tempFile, ); }, ); }
3. 页面增加loading状态处理
build方法里先判断加载状态,避免列表未初始化时报错:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( automaticallyImplyLeading: false, title: Text('Data'), actions: [ RaisedButton( child: Text('Logout'), onPressed: () { Navigator.popUntil(context, ModalRoute.withName('/')); }, ), RaisedButton( child: Text('Main'), onPressed: () { sendUsername(context); }, ), RaisedButton( child: Text('Charts'), onPressed: () { sendUsernameToChart(context); }, ), ], ), body: isLoading ? const Center(child: CircularProgressIndicator()) : ListView.builder( itemBuilder: (context, i) { return Card( child: Row( children: [ Image.file( listLokasiConvert[i].image, width: 100, height: 100, fit: BoxFit.cover, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(listLokasiConvert[i].name), Text(listLokasiConvert[i].category), Text(listLokasiConvert[i].description), Text('Coordinates: ${listLokasiConvert[i].latitude}, ${listLokasiConvert[i].longitude}'), ], ) ], ), ); }, itemCount: listLokasiConvert.length, ), ); }
4. 可选优化:DatabaseHelper单例初始化优化
避免重复调用initDatabase,可以把数据库实例改成懒加载:
class DatabaseHelper { static final _instance = DatabaseHelper._internal(); DatabaseHelper._internal(); factory DatabaseHelper() => _instance; Database? _db; Future<Database> get db async { if (_db != null) return _db!; _db = await _initDatabase(); return _db!; } Future<Database> _initDatabase() async { var databasePath = await getDatabasesPath(); var path = join(databasePath, 'table.db'); return await openDatabase(path, version: 1, onCreate: onCreate); } Future<void> onCreate(Database db, int version) async { await db.execute(''' CREATE TABLE lokasi (id INTEGER PRIMARY KEY, name TEXT, description TEXT, category TEXT, latitude REAL, longitude REAL, image BLOB) '''); } Future<Lokasi> insert(Lokasi lokasi) async { final dbClient = await db; await dbClient.insert('lokasi', lokasi.toJson()); return lokasi; } Future<List<Lokasi>> query() async { final dbClient = await db; var list = await dbClient.query('lokasi'); return List.generate(list.length, (i) => Lokasi.fromJson(list[i])); } }
调整后不需要主动调用initDatabase,直接调用db.query即可自动完成初始化。
内容的提问来源于stack exchange,提问作者Ignas Prasetyo
相关产品推荐
相关产品推荐

