Flutter使用SQLite实现收藏功能 控制心形图标显示状态
Flutter + SQLite 收藏功能实现方案
实现逻辑分三部分:补全数据库增删查方法、绑定列表项初始收藏状态、实现点击切换收藏/取消收藏逻辑。
1. 补全数据库操作方法
首先建议给favourite表添加联合唯一约束(字段为bookid, chapternumber, versenumber),避免同一条内容重复插入收藏表。
替换原有不完整的数据库方法,统一用参数化查询避免SQL注入风险:
// 检查单条内容是否已收藏 static Future<bool> isFavorited(int bookid, int chapternumber, int versenumber) async { final db = await Dbhelper().db; final res = await db!.rawQuery( 'SELECT COUNT(*) as count FROM favourite WHERE bookid = ? AND chapternumber = ? AND versenumber = ?', [bookid, chapternumber, versenumber] ); return res.first['count'] as int > 0; } // 新增收藏 static Future<void> addFavourite(String bookname, int chapternumber, int versenumber, String verse, int bookid) async { final db = await Dbhelper().db; final exist = await isFavorited(bookid, chapternumber, versenumber); if (!exist) { await db!.rawInsert( 'INSERT INTO favourite(bookname, chapternumber, versenumber, verse, bookid) VALUES(?, ?, ?, ?, ?)', [bookname, chapternumber, versenumber, verse, bookid] ); } } // 取消收藏 static Future<void> removeFavourite(int bookid, int chapternumber, int versenumber) async { final db = await Dbhelper().db; await db!.rawDelete( 'DELETE FROM favourite WHERE bookid = ? AND chapternumber = ? AND versenumber = ?', [bookid, chapternumber, versenumber] ); }
原有字符串拼接SQL的写法存在注入风险,所有带动态参数的SQL语句统一用
?做占位符,通过参数数组传值。
2. 列表项初始状态加载
不要全量查询所有收藏数据再遍历匹配,性能差。每个列表项单独初始化查询自身的收藏状态即可:
在列表项对应的State类中定义两个状态变量:
late bool _isFavorite; bool _isLoading = true;
在initState生命周期中加载当前项的收藏状态:
@override void initState() { super.initState(); _loadInitStatus(); } Future<void> _loadInitStatus() async { final status = await Dbhelper.isFavorited( allverse[index]['id'], widget.chapternumber, allverse[index]['v'] ); if (mounted) { setState(() { _isFavorite = status; _isLoading = false; }); } }
3. 收藏按钮逻辑替换
原有按钮仅实现了插入逻辑,替换为以下代码,绑定初始状态、实现点击切换收藏/取消收藏:
// 加载状态下显示灰色空心心形占位,避免图标闪烁 if (_isLoading) { return const Icon(Icons.favorite_border, color: Colors.grey, size: 30); } return FavoriteButton( iconDisabledColor: Colors.grey, iconSize: 30, isFavorite: _isFavorite, // 绑定初始收藏状态 valueChanged: (newStatus) async { // 先更新UI状态,保证交互流畅 setState(() { _isFavorite = newStatus; }); try { if (newStatus) { // 状态为已收藏,执行插入 await Dbhelper.addFavourite( widget.bookname, widget.chapternumber, allverse[index]['v'], allverse[index]['t'], allverse[index]['id'] ); } else { // 状态为未收藏,执行删除 await Dbhelper.removeFavourite( allverse[index]['id'], widget.chapternumber, allverse[index]['v'] ); } } catch (e) { // 数据库操作失败时回滚UI状态 setState(() { _isFavorite = !newStatus; }); } }, );
性能优化(可选)
如果经文列表单页数据量超过100条,可以在拉取经文列表时直接左连接收藏表,一次性查出所有条目的收藏状态,省去单条查询的IO开销,对应SQL示例:
SELECT DISTINCT t.id, t.v, t.t, CASE WHEN f.id IS NULL THEN 0 ELSE 1 END as is_favorited FROM ${database} t LEFT JOIN favourite f ON t.b = f.bookid AND t.c = f.chapternumber AND t.v = f.versenumber WHERE t.b = ${booknumber} AND t.c = ${chapternumber}
查询结果中自带is_favorited字段,直接绑定到_isFavorite变量即可,不需要单独做初始化查询。另外可以给favourite表的bookid、chapternumber、versenumber三个字段加索引,进一步提升查询速度。
内容的提问来源于stack exchange,提问作者aneel ilyas
相关产品推荐
相关产品推荐

