You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 22:54:25