Flutter中如何等待Sqflite加载数据后再构建ListView?
解决数据库异步加载前构建ListView的问题
你的问题核心在于:数据库查询是异步操作,当current_tasks.dart初始化并尝试构建ListView时,全局变量tasks还没被赋值(依然是null),所以直接访问length或者强行用!都会报错。而且全局变量更新后不会自动触发UI刷新,即便后续tasks被赋值,UI也不会同步更新。
这里给你两种实用的解决方案:
方案一:用FutureBuilder处理异步UI(推荐)
FutureBuilder是Flutter专门用来处理异步数据与UI绑定的组件,它会自动监听异步任务的状态,根据不同状态渲染对应的UI。
- 先把数据库查询封装成一个独立的Future函数:
// 可以放在你的数据库工具类或者constants.dart里 Future<List<Map<dynamic, dynamic>>> fetchTasks() async { final db = await openDatabase( // 这里填你的数据库路径、版本、创建表逻辑等配置 version: 1, onCreate: (db, version) { // 创建tasks表的逻辑 }, ); return getFromDatabase(db); }
- 在
current_tasks.dart中用FutureBuilder构建ListView:
class CurrentTasksScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: FutureBuilder<List<Map<dynamic, dynamic>>>( future: fetchTasks(), builder: (context, snapshot) { // 任务正在加载中 if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } // 加载出错 if (snapshot.hasError) { return Center(child: Text('加载任务失败:${snapshot.error}')); } // 数据加载完成,空数据时返回空列表避免报错 final tasks = snapshot.data ?? []; return ListView.builder( itemCount: tasks.length, itemBuilder: (context, index) { // 根据你的数据结构构建列表项 return ListTile( title: Text(tasks[index]['task_title'].toString()), subtitle: Text(tasks[index]['task_date'].toString()), ); }, ); }, ), ); } }
方案二:用StatefulWidget手动管理状态
如果你更习惯手动控制状态,可以用StatefulWidget在初始化阶段异步加载数据,通过setState触发UI更新。
class CurrentTasksScreen extends StatefulWidget { @override State<CurrentTasksScreen> createState() => _CurrentTasksScreenState(); } class _CurrentTasksScreenState extends State<CurrentTasksScreen> { List<Map<dynamic, dynamic>>? _tasks; bool _isLoading = true; String? _loadError; @override void initState() { super.initState(); // 页面初始化时加载数据 _loadTasksFromDb(); } Future<void> _loadTasksFromDb() async { try { final db = await openDatabase( // 你的数据库配置 version: 1, onCreate: (db, version) {}, ); final fetchedTasks = await getFromDatabase(db); setState(() { _tasks = fetchedTasks; _isLoading = false; }); } catch (e) { setState(() { _loadError = e.toString(); _isLoading = false; }); } } @override Widget build(BuildContext context) { if (_isLoading) { return const Center(child: CircularProgressIndicator()); } if (_loadError != null) { return Center(child: Text('加载失败:$_loadError')); } // 空数据时用空列表兜底 final tasks = _tasks ?? []; return ListView.builder( itemCount: tasks.length, itemBuilder: (context, index) { return ListTile(title: Text(tasks[index]['title'].toString())); }, ); } }
注意事项
- 不要再用全局变量存储需要驱动UI更新的数据:全局变量的变化不会触发Flutter的UI重建,即便后续
tasks被赋值,页面也不会自动刷新。 - 始终处理空数据和异常情况:比如用
?? []给空数据兜底,避免空指针报错;同时捕获异步操作的异常,给用户友好的提示。
内容的提问来源于stack exchange,提问作者Osama Amr
相关产品推荐
相关产品推荐

