一、为什么选择Dart和SQLite这对黄金搭档
在移动开发领域,本地数据存储就像是你手机里的记事本——随时要存,随时要取,还得保证速度快、不卡顿。Dart作为Flutter的御用语言,搭配轻量级数据库SQLite,简直是天作之合。SQLite不需要服务器,单个文件就能搞定所有数据存储,而Dart通过sqflite插件可以轻松操作它。
举个实际场景:你正在开发一个记账App,用户每次输入收支记录都需要立刻保存,这时候网络可能不稳定,本地存储就成了刚需。下面这段代码展示了如何初始化SQLite数据库:
// 技术栈:Flutter + Dart + sqflite
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
Future<Database> initDatabase() async {
// 获取数据库存储路径
final dbPath = await getDatabasesPath();
final path = join(dbPath, 'my_account.db');
// 打开数据库(如果不存在则创建)
return openDatabase(
path,
version: 1,
onCreate: (db, version) {
// 创建记账记录表
return db.execute('''
CREATE TABLE accounts(
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT,
amount REAL,
category TEXT,
date TEXT
)
''');
},
);
}
注释说明:
getDatabasesPath():获取设备上的数据库存储目录AUTOINCREMENT:让主键ID自动增长,避免手动管理
二、CRUD操作:从入门到熟练
数据库的核心操作无非增删改查(CRUD),但细节决定体验。比如插入数据时要考虑事务,查询时要支持排序和分页。下面是一个完整的操作示例:
// 插入一条记账记录(带事务)
Future<void> insertRecord(Account record) async {
final db = await initDatabase();
await db.transaction((txn) async {
await txn.insert(
'accounts',
record.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
});
}
// 查询最近10条支出记录(按日期倒序)
Future<List<Account>> queryRecentExpenses() async {
final db = await initDatabase();
final maps = await db.query(
'accounts',
where: 'amount < 0', // 支出为负数
orderBy: 'date DESC',
limit: 10,
);
return maps.map((map) => Account.fromMap(map)).toList();
}
避坑指南:
- 始终用
transaction处理批量操作,避免数据不一致 ConflictAlgorithm.replace可以在主键冲突时自动覆盖旧数据
三、高级技巧:数据库迁移与性能优化
当你发布新版本需要修改表结构时,直接删库用户会骂街的。正确的做法是数据库迁移。比如我们要给记账表增加tag字段:
Future<void> migrateDatabase(Database db, int oldVersion, int newVersion) async {
if (oldVersion < 2) {
await db.execute('ALTER TABLE accounts ADD COLUMN tag TEXT DEFAULT ""');
}
}
// 初始化时指定迁移回调
openDatabase(
path,
version: 2, // 版本号升级
onUpgrade: migrateDatabase,
);
性能优化三原则:
- 用
batch批量操作比单条执行快10倍以上 - 频繁查询的字段记得加索引
- 关闭数据库时调用
close()防止内存泄漏
四、实战中的那些坑与解决方案
- 并发问题:多个页面同时操作数据库可能导致锁冲突。解决方案是用单例模式管理数据库连接:
class DatabaseHelper {
static Database? _db;
Future<Database> get database async {
if (_db != null) return _db!;
_db = await initDatabase();
return _db!;
}
}
- 数据类型映射:SQLite只有5种基础类型,处理日期时需要转换:
// 存储时转为字符串
record.toMap()..['date'] = record.date.toIso8601String();
// 读取时解析回来
Account.fromMap(map)..date = DateTime.parse(map['date']);
- 加密需求:敏感数据可以使用
sqlcipher插件加密整个数据库文件
五、什么时候该用SQLite?什么时候该换方案?
适合场景:
- 个人离线应用(如日记、记账)
- 需要复杂查询的中小型数据集(<10万条)
- 要求毫秒级响应速度的读写操作
不适合场景:
- 需要多设备同步的数据(考虑Firebase)
- 超大规模数据存储(考虑Hive或对象存储)
- 高频写入的日志类数据(考虑文件直接存储)
六、总结
Dart和SQLite的组合就像咖啡配奶糖——简单却能调出专业味道。记住几个关键点:事务保证安全、迁移保持兼容、索引提升速度。下次当你需要在Flutter应用中存储用户数据时,不妨先试试这个轻量级方案,它可能会给你意想不到的惊喜。
评论