Flutter 学习之旅 之 flutter 使用 SQLite(sqflite) 实现简单的数据本地化 保存/获取/移除/判断是否存在 的简单封装
目录
Flutter 学习之旅 之 flutter 使用 SQLite(sqflite) 实现简单的数据本地化 保存/获取/移除/判断是否存在 的简单封装
一、简单介绍
2. Path_provider
3. SQLite
二、sqflite
三、安装 sqflite
四、简单案例实现
五、关键代码
一、简单介绍
Flutter 是一款开源的 UI 软件开发工具包,由 Google 开发和维护。它允许开发者使用一套代码同时构建跨平台的应用程序,包括移动设备(iOS 和 Android)、Web 和桌面平台(Windows、macOS 和 Linux)。
Flutter 使用 Dart 编程语言,它可以将代码编译为 ARM 或 Intel 机器代码以及 JavaScript,从而实现快速的性能。Flutter 提供了一个丰富的预置小部件库,开发者可以根据自己的需求灵活地控制每个像素,从而创建自定义的、适应性强的设计,这些设计在任何屏幕上都能呈现出色的外观和感觉。
在Flutter中,进行本地化保存的方法主要有以下几种:
1. SharedPreferences
SharedPreferences
是一种轻量级的存储方案,适用于存储简单的键值对数据,如字符串、整数、布尔值等。它类似于Android中的
SharedPreferences
或iOS中的UserDefaults
,使用起来非常方便。
优点:简单易用,适合存储少量数据。
缺点:只能存储基本数据类型,不适合存储大量数据
。
2. Path_provider
path_provider
插件提供了一种平台无关的方式,用于访问设备的文件系统。它支持访问两种文件位置系统:
临时文件夹:系统可以随时清空的临时(缓存)文件夹。
Documents目录:供应用使用,只有在删除应用时,系统才会清除这个目录
。
通过
path_provider
,你可以将数据以文件的形式保存到本地,适合存储一些持久化数据或从网络下载的数据。
3. SQLite
SQLite
是一种轻量级的关系型数据库,适用于存储结构化的数据。它提供了强大的数据查询和管理功能,适合存储大量数据。
优点:功能强大,支持复杂的数据结构和查询。
缺点:使用相对复杂,需要编写SQL语句
。
二、sqflite
sqflite
是一个用于 Flutter 的 SQLite 数据库插件,允许开发者在移动应用中存储和管理本地数据。它提供了简单易用的 API,支持创建数据库、执行 SQL 查询、插入、更新、删除等操作。sqflite
基于 SQLite,适合存储结构化数据,如用户信息、设置等。它支持 Android 和 iOS 平台,通过 Dart 语言操作,方便与 Flutter 应用集成。使用 sqflite
可以实现数据的持久化存储,提升应用的性能和用户体验。
在开发和使用
sqflite
时,需要注意以下几点以确保代码的正确性和性能优化:1. 初始化数据库
确保初始化:在使用
sqflite
之前,必须先初始化数据库。通常在应用启动时调用WidgetsFlutterBinding.ensureInitialized()
,然后初始化数据库实例。数据库路径:使用
getDatabasesPath()
获取数据库存储路径,确保路径正确。2. 数据库版本管理
版本控制:通过
version
参数管理数据库版本。如果需要更新数据库结构,可以在onUpgrade
回调中编写升级逻辑。兼容性:确保升级逻辑兼容旧版本数据,避免数据丢失。
3. 表结构设计
主键设计:合理设计表的主键,避免重复和冲突。例如,使用
INTEGER PRIMARY KEY AUTOINCREMENT
或TEXT PRIMARY KEY
。字段类型:选择合适的字段类型,如
TEXT
、INTEGER
、REAL
和BLOB
,以优化存储和查询性能。4. 事务管理
使用事务:对于多个操作(如批量插入或更新),使用事务可以提高性能并确保数据一致性。
await db.transaction((txn) async { await txn.insert('table', data1); await txn.insert('table', data2); });
5. 查询优化
索引:为经常查询的字段创建索引,提高查询效率。
await db.execute('CREATE INDEX idx_column ON table (column)');
避免全表扫描:尽量使用
WHERE
子句进行条件查询,避免全表扫描。6. 错误处理
捕获异常:在数据库操作中捕获异常,避免应用崩溃。
try { await db.insert('table', data); } catch (e) { print('Error: $e'); }
7. 资源管理
关闭数据库:在应用退出或不再需要时关闭数据库连接,释放资源。
await db.close();
8. 异步操作
使用
await
:所有数据库操作都是异步的,使用await
确保操作完成后再继续执行。避免阻塞主线程:确保数据库操作不会阻塞主线程,影响用户体验。
9. 测试
单元测试:编写单元测试验证数据库操作的正确性。
模拟数据:在测试环境中使用模拟数据,避免依赖真实数据。
10. 安全性
SQL 注入:使用参数化查询,避免 SQL 注入攻击。
await db.query('table', where: 'column = ?', whereArgs: [value]);
11. 备份和恢复
备份数据:定期备份数据库文件,防止数据丢失。
恢复数据:提供恢复机制,允许用户从备份中恢复数据。
12. 日志和调试
启用日志:在开发过程中启用日志,方便调试。
await db.execute('PRAGMA journal_mode=WAL');
通过遵循这些注意事项,可以确保
sqflite
的使用更加高效、稳定和安全,提升应用的整体质量。
三、安装 sqflite
1、直接运行命令:flutter pub add sqflite
2、或者在 pubspec.yaml 添加
dependencies:
sqflite: ^2.4.2
四、简单案例实现
1、这里使用 Android Studio 进行创建 Flutter 项目
2、创建一个 application 的 Flutter 项目
3、项目结构如下
4、编写一个 DatabaseHelper
5、在 main 中编写一个测试代码
6、运行打印如下
7、继续编写一个复杂些的
8、在 main 中编写一个测试代码
9、运行打印如下
五、关键代码
1、DatabaseHelper 一
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseHelper {
// 数据库文件名
static const String dbName = "localization.db";
// 数据库版本号,用于版本控制和升级
static const int dbVersion = 1;
// 数据表名,用于存储本地化数据
static const String tableLocalization = "localization";
// 数据表中的键字段名
static const String columnKey = "key";
// 数据表中的值字段名
static const String columnValue = "value";
// 私有数据库实例变量,用于存储数据库连接
Database? _database;
// 获取数据库实例的公共方法
// 如果数据库已经初始化,则直接返回;否则初始化数据库并返回
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
// 初始化数据库的方法
Future<Database> _initDatabase() async {
// 获取设备的数据库存储路径
String path = join(await getDatabasesPath(), dbName);
// 打开或创建数据库文件
// 如果数据库不存在,则会调用 onCreate 回调方法来创建表
return await openDatabase(path, version: dbVersion, onCreate: _onCreate);
}
// 数据库创建时调用的回调方法
// 用于创建数据表结构
Future<void> _onCreate(Database db, int version) async {
// 执行 SQL 语句,创建 localization 表
// 表包含两个字段:key(主键,唯一)和 value
await db.execute('''
CREATE TABLE $tableLocalization (
$columnKey TEXT PRIMARY KEY, // 键字段,作为主键
$columnValue TEXT // 值字段
)
''');
}
// 保存本地化数据的方法
Future<void> saveLocalization(String key, String value) async {
// 获取数据库实例
Database db = await database;
// 插入数据到 localization 表
// 如果 key 已存在,则使用 ConflictAlgorithm.replace 策略替换原有数据
await db.insert(tableLocalization, {columnKey: key, columnValue: value},
conflictAlgorithm: ConflictAlgorithm.replace);
}
// 获取本地化数据的方法
Future<String?> getLocalization(String key) async {
// 获取数据库实例
Database db = await database;
// 查询 localization 表,查找指定 key 的数据
List<Map<String, dynamic>> result = await db.query(tableLocalization,
where: '$columnKey = ?', whereArgs: [key]);
// 如果查询结果不为空,返回 value 字段的值;否则返回 null
return result.isNotEmpty ? result.first[columnValue] as String? : null;
}
// 移除本地化数据的方法
Future<void> removeLocalization(String key) async {
// 获取数据库实例
Database db = await database;
// 从 localization 表中删除指定 key 的数据
await db.delete(tableLocalization, where: '$columnKey = ?', whereArgs: [key]);
}
// 判断是否存在指定 key 的本地化数据的方法
Future<bool> hasLocalization(String key) async {
// 获取数据库实例
Database db = await database;
// 查询 localization 表,查找指定 key 的数据
List<Map<String, dynamic>> result = await db.query(tableLocalization,
where: '$columnKey = ?', whereArgs: [key]);
// 如果查询结果不为空,返回 true;否则返回 false
return result.isNotEmpty;
}
}
代码说明
常量定义:
dbName
:定义数据库文件的名称。
dbVersion
:定义数据库的版本号,用于版本控制和升级。
tableLocalization
:定义存储本地化数据的表名。
columnKey
和columnValue
:定义表中的字段名,分别用于存储键和值。数据库初始化:
_initDatabase
方法通过getDatabasesPath
获取设备的数据库存储路径,并使用openDatabase
打开或创建数据库文件。如果数据库文件不存在,则会调用
_onCreate
方法来创建表结构。表结构创建:
_onCreate
方法中,使用 SQL 语句创建localization
表,包含两个字段:key
(主键,唯一)和value
。数据操作方法:
saveLocalization
:插入或更新数据到localization
表。
getLocalization
:查询指定key
的数据,返回对应的value
。
removeLocalization
:删除指定key
的数据。
hasLocalization
:判断是否存在指定key
的数据。通过这些注释,你可以更清晰地理解每个方法的作用和实现逻辑,便于在项目中使用和维护。
2、main 一
import 'package:flutter/material.dart'; // 导入 Flutter 的 Material 组件库
import 'package:test_sqflite_0317/database_helper.dart'; // 导入封装的 DatabaseHelper 类
void main() async {
// 确保 Flutter 绑定初始化
// 在使用 Flutter 的异步操作(如数据库操作)之前,需要调用此方法来初始化 Flutter 的绑定。
// 这是 Flutter 应用程序启动时的必要步骤,确保可以使用 Flutter 提供的异步功能。
WidgetsFlutterBinding.ensureInitialized();
// 创建 DatabaseHelper 的实例
// DatabaseHelper 是一个封装类,用于管理本地化数据的保存、获取、移除等操作。
var dbHelper = DatabaseHelper();
// 保存数据到数据库
// 调用 DatabaseHelper 的 saveLocalization 方法,将键 'language' 和值 'en' 保存到数据库中。
// 这里的 'language' 是一个示例键,'en' 是对应的值,表示语言设置为英语。
await dbHelper.saveLocalization('language', 'en');
// 从数据库中获取保存的数据
// 调用 DatabaseHelper 的 getLocalization 方法,通过键 'language' 获取对应的值。
// 如果数据库中存在该键,则返回对应的值;否则返回 null。
String? language = await dbHelper.getLocalization('language');
// 打印获取到的语言设置
// 如果成功获取到值,则打印 'Saved language: en';否则打印 'Saved language: null'。
print('Saved language: $language');
// 判断数据库中是否有保存的数据
// 调用 DatabaseHelper 的 hasLocalization 方法,检查键 'language' 是否存在于数据库中。
// 如果存在,返回 true;否则返回 false。
bool hasLanguage = await dbHelper.hasLocalization('language');
// 打印判断结果
// 如果存在该键,则打印 'Has language: true';否则打印 'Has language: false'。
print('Has language: $hasLanguage');
// 从数据库中移除保存的数据
// 调用 DatabaseHelper 的 removeLocalization 方法,通过键 'language' 删除对应的记录。
await dbHelper.removeLocalization('language');
// 再次判断数据库中是否有保存的数据
// 再次调用 hasLocalization 方法,检查键 'language' 是否还存在于数据库中。
hasLanguage = await dbHelper.hasLocalization('language');
// 打印判断结果
// 由于已经删除了该键,因此打印 'Has language after removal: false'。
print('Has language after removal: $hasLanguage');
}
代码说明
导入必要的库:
flutter/material.dart
:导入 Flutter 的 Material 组件库,用于构建 Flutter 应用。
test_sqflite_0317/database_helper.dart
:导入封装的DatabaseHelper
类,该类提供了数据库操作的接口。初始化 Flutter 绑定:
WidgetsFlutterBinding.ensureInitialized()
:在使用 Flutter 的异步操作之前,必须调用此方法来初始化 Flutter 的绑定。这是 Flutter 应用程序启动时的必要步骤。创建
DatabaseHelper
实例:
var dbHelper = DatabaseHelper()
:创建DatabaseHelper
的实例,用于后续的数据库操作。保存数据:
await dbHelper.saveLocalization('language', 'en')
:调用DatabaseHelper
的saveLocalization
方法,将键'language'
和值'en'
保存到数据库中。获取保存的数据:
String? language = await dbHelper.getLocalization('language')
:调用DatabaseHelper
的getLocalization
方法,通过键'language'
获取对应的值。
print('Saved language: $language')
:打印获取到的语言设置。判断是否有保存的数据:
bool hasLanguage = await dbHelper.hasLocalization('language')
:调用DatabaseHelper
的hasLocalization
方法,检查键'language'
是否存在于数据库中。
print('Has language: $hasLanguage')
:打印判断结果。移除保存的数据:
await dbHelper.removeLocalization('language')
:调用DatabaseHelper
的removeLocalization
方法,通过键'language'
删除对应的记录。再次判断是否有保存的数据:
hasLanguage = await dbHelper.hasLocalization('language')
:再次调用hasLocalization
方法,检查键'language'
是否还存在于数据库中。
print('Has language after removal: $hasLanguage')
:打印判断结果。通过这些注释,你可以更清晰地理解代码的逻辑和功能,便于在项目中使用和维护。
3、DatabaseHelper 二
import 'package:sqflite/sqflite.dart'; // 导入 sqflite 包,用于操作 SQLite 数据库
import 'package:path/path.dart'; // 导入 path 包,用于处理文件路径
class DatabaseHelper {
// 数据库文件名
static const String dbName = "user_management.db";
// 数据库版本号,用于版本控制和升级
static const int dbVersion = 1;
// 数据表名,用于存储用户信息
static const String tableUsers = "users";
// 数据表中的用户ID字段名,主键
static const String columnId = "id";
// 数据表中的用户姓名字段名
static const String columnName = "name";
// 数据表中的用户邮箱字段名,唯一约束
static const String columnEmail = "email";
// 私有数据库实例变量,用于存储数据库连接
Database? _database;
// 获取数据库实例的公共方法
// 如果数据库已经初始化,则直接返回;否则初始化数据库并返回
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}
// 初始化数据库的方法
// 获取数据库存储路径,并打开或创建数据库文件
Future<Database> _initDatabase() async {
// 使用 getDatabasesPath 获取设备的数据库存储路径
String path = join(await getDatabasesPath(), dbName);
// 打开或创建数据库文件,指定版本号和 onCreate 回调方法
return await openDatabase(path, version: dbVersion, onCreate: _onCreate);
}
// 数据库创建时调用的回调方法
// 用于创建数据表结构
Future<void> _onCreate(Database db, int version) async {
// 执行 SQL 语句,创建 users 表
// 表包含三个字段:id(自增主键)、name(非空)、email(非空且唯一)
await db.execute('''
CREATE TABLE $tableUsers (
$columnId INTEGER PRIMARY KEY AUTOINCREMENT, // 用户ID,自增主键
$columnName TEXT NOT NULL, // 用户姓名,非空
$columnEmail TEXT NOT NULL UNIQUE // 用户邮箱,非空且唯一
)
''');
}
// 插入用户数据的方法
Future<int> insertUser(Map<String, dynamic> user) async {
// 获取数据库实例
Database db = await database;
// 插入数据到 users 表,返回插入的行ID
return await db.insert(tableUsers, user);
}
// 查询所有用户的方法
Future<List<Map<String, dynamic>>> getAllUsers() async {
// 获取数据库实例
Database db = await database;
// 查询 users 表,返回所有用户数据
return await db.query(tableUsers);
}
// 根据ID查询用户的方法
Future<Map<String, dynamic>?> getUserById(int id) async {
// 获取数据库实例
Database db = await database;
// 查询 users 表,查找指定ID的用户
List<Map<String, dynamic>> result = await db.query(
tableUsers,
where: '$columnId = ?', // 查询条件
whereArgs: [id], // 查询参数
);
// 如果查询结果不为空,返回第一个结果;否则返回 null
return result.isNotEmpty ? result.first : null;
}
// 更新用户数据的方法
Future<int> updateUser(Map<String, dynamic> user) async {
// 获取数据库实例
Database db = await database;
// 更新 users 表中的指定用户数据,返回更新的行数
return await db.update(
tableUsers,
user,
where: '$columnId = ?', // 更新条件
whereArgs: [user[columnId]], // 更新参数
);
}
// 删除用户的方法
Future<int> deleteUser(int id) async {
// 获取数据库实例
Database db = await database;
// 删除 users 表中的指定用户,返回删除的行数
return await db.delete(
tableUsers,
where: '$columnId = ?', // 删除条件
whereArgs: [id], // 删除参数
);
}
// 关闭数据库的方法
Future<void> closeDatabase() async {
// 如果数据库实例不为空,则关闭数据库连接
if (_database != null) {
await _database!.close();
_database = null;
}
}
}
代码说明
常量定义:
dbName
:定义数据库文件的名称。
dbVersion
:定义数据库的版本号,用于版本控制和升级。
tableUsers
:定义存储用户信息的表名。
columnId
、columnName
和columnEmail
:定义表中的字段名。数据库实例:
_database
:私有变量,用于存储数据库连接实例。获取数据库实例:
database
:公共方法,用于获取数据库实例。如果数据库已经初始化,则直接返回;否则调用_initDatabase
初始化数据库。初始化数据库:
_initDatabase
:获取数据库存储路径,并打开或创建数据库文件。如果数据库不存在,则调用_onCreate
方法创建表结构。创建表:
_onCreate
:创建users
表,包含三个字段:id
(自增主键)、name
(非空)、数据操作方法:
insertUser
:插入用户数据到users
表,返回插入的行ID。
getAllUsers
:查询users
表,返回所有用户数据。
getUserById
:根据用户ID查询用户数据,返回查询结果或null
。
updateUser
:更新指定用户的数据,返回更新的行数。
deleteUser
:删除指定用户,返回删除的行数。
closeDatabase
:关闭数据库连接,释放资源。通过这些注释,你可以更清晰地理解代码的逻辑和功能,便于在项目中使用和维护。
4、main 二
import 'package:flutter/material.dart'; // 导入 Flutter 的 Material 组件库
import 'database_helper.dart'; // 导入封装的 DatabaseHelper 类
void main() async {
// 确保 Flutter 绑定初始化
// 在使用 Flutter 的异步操作(如数据库操作)之前,必须调用此方法来初始化 Flutter 的绑定。
// 这是 Flutter 应用程序启动时的必要步骤,确保可以使用 Flutter 提供的异步功能。
WidgetsFlutterBinding.ensureInitialized();
// 创建 DatabaseHelper 的实例
// DatabaseHelper 是一个封装类,用于管理用户数据的保存、查询、更新和删除等操作。
var dbHelper = DatabaseHelper();
// 插入用户数据
// 调用 DatabaseHelper 的 insertUser 方法,将用户数据(姓名和邮箱)插入到数据库中。
// 这里插入了一个示例用户,姓名为 'John Doe',邮箱为 'john.doe@example.com'。
await dbHelper.insertUser({
'name': 'John Doe',
'email': 'john.doe@example.com'
});
// 查询所有用户
// 调用 DatabaseHelper 的 getAllUsers 方法,查询数据库中的所有用户数据。
// 返回的是一个包含所有用户信息的列表,每个用户信息是一个 Map。
List<Map<String, dynamic>> users = await dbHelper.getAllUsers();
// 打印所有用户数据
print('All users: $users');
// 根据ID查询用户
// 调用 DatabaseHelper 的 getUserById 方法,通过用户ID查询特定用户的数据。
// 这里查询ID为1的用户。
Map<String, dynamic>? user = await dbHelper.getUserById(1);
// 打印查询到的用户数据
print('User with ID 1: $user');
// 更新用户数据
// 调用 DatabaseHelper 的 updateUser 方法,更新特定用户的数据。
// 这里将ID为1的用户姓名更新为 'Jane Doe',邮箱更新为 'jane.doe@example.com'。
await dbHelper.updateUser({
'id': 1,
'name': 'Jane Doe',
'email': 'jane.doe@example.com'
});
// 查询更新后的用户
// 再次调用 getUserById 方法,查询更新后的用户数据。
user = await dbHelper.getUserById(1);
// 打印更新后的用户数据
print('Updated user with ID 1: $user');
// 删除用户
// 调用 DatabaseHelper 的 deleteUser 方法,删除特定用户。
// 这里删除ID为1的用户。
await dbHelper.deleteUser(1);
// 查询所有用户(验证删除操作)
// 再次调用 getAllUsers 方法,查询数据库中的所有用户数据,验证删除操作是否成功。
users = await dbHelper.getAllUsers();
// 打印删除后的所有用户数据
print('All users after deletion: $users');
// 关闭数据库
// 调用 DatabaseHelper 的 closeDatabase 方法,关闭数据库连接,释放资源。
await dbHelper.closeDatabase();
}
代码说明
导入必要的库:
flutter/material.dart
:导入 Flutter 的 Material 组件库,用于构建 Flutter 应用。
database_helper.dart
:导入封装的DatabaseHelper
类,该类提供了数据库操作的接口。初始化 Flutter 绑定:
WidgetsFlutterBinding.ensureInitialized()
:在使用 Flutter 的异步操作之前,必须调用此方法来初始化 Flutter 的绑定。这是 Flutter 应用程序启动时的必要步骤。创建
DatabaseHelper
实例:
var dbHelper = DatabaseHelper()
:创建DatabaseHelper
的实例,用于后续的数据库操作。插入用户数据:
await dbHelper.insertUser({...})
:调用DatabaseHelper
的insertUser
方法,将用户数据(姓名和邮箱)插入到数据库中。查询所有用户:
List<Map<String, dynamic>> users = await dbHelper.getAllUsers()
:调用DatabaseHelper
的getAllUsers
方法,查询数据库中的所有用户数据。
print('All users: $users')
:打印所有用户数据。根据ID查询用户:
Map<String, dynamic>? user = await dbHelper.getUserById(1)
:调用DatabaseHelper
的getUserById
方法,通过用户ID查询特定用户的数据。
print('User with ID 1: $user')
:打印查询到的用户数据。更新用户数据:
await dbHelper.updateUser({...})
:调用DatabaseHelper
的updateUser
方法,更新特定用户的数据。查询更新后的用户:
user = await dbHelper.getUserById(1)
:再次调用getUserById
方法,查询更新后的用户数据。
print('Updated user with ID 1: $user')
:打印更新后的用户数据。删除用户:
await dbHelper.deleteUser(1)
:调用DatabaseHelper
的deleteUser
方法,删除特定用户。查询所有用户(验证删除操作):
users = await dbHelper.getAllUsers()
:再次调用getAllUsers
方法,查询数据库中的所有用户数据,验证删除操作是否成功。
print('All users after deletion: $users')
:打印删除后的所有用户数据。关闭数据库:
await dbHelper.closeDatabase()
:调用DatabaseHelper
的closeDatabase
方法,关闭数据库连接,释放资源。通过这些注释,你可以更清晰地理解代码的逻辑和功能,便于在项目中使用和维护。