如何在Flutter中使用SQLite?(含代码示例)

Flutter 中 SQLite 的使用详解

在 Flutter 应用中,如果需要本地存储结构化数据,SQLite 是一个非常好的选择。SQLite 是一个轻量级的、嵌入式的关系型数据库,无需单独的服务器进程,直接在应用内部进行数据操作。本文将详细介绍如何在 Flutter 项目中使用 SQLite,并提供完整的代码示例。

1. 添加依赖

首先,我们需要在 pubspec.yaml 文件中添加 sqflitepath_provider 插件的依赖:

yaml
dependencies:
flutter:
sdk: flutter
sqflite: ^2.0.0+3 # 使用最新版本
path_provider: ^2.0.0 # 使用最新版本

解释:
* sqflite: Flutter 官方推荐的 SQLite 插件,提供了操作 SQLite 数据库的 API。
* path_provider: 用于获取应用的文件系统路径,例如文档目录(用于存储数据库文件)。

添加依赖后,在终端运行 flutter pub get 命令来安装这些插件。

2. 创建数据库帮助类 (Database Helper)

为了更好地组织代码和管理数据库操作,我们通常会创建一个数据库帮助类。这个类将负责:

  • 打开/创建数据库
  • 定义数据表结构 (创建表)
  • 执行 CRUD 操作 (Create, Read, Update, Delete)

以下是一个 DatabaseHelper 类的示例:

```dart
import 'dart:async';
import 'dart:io';

import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';

class DatabaseHelper {
// 单例模式
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();

static Database? _database;

// 获取数据库,如果不存在则创建
Future get database async {
if (_database != null) return _database!;
_database = await _initDatabase();
return _database!;
}

// 初始化数据库
Future _initDatabase() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, 'my_database.db'); // 数据库文件名
return await openDatabase(
path,
version: 1, // 数据库版本号,用于数据库升级
onCreate: _onCreate, // 创建数据库表的回调函数
);
}

// 创建数据库表
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE dogs (
id INTEGER PRIMARY KEY,
name TEXT,
age INTEGER
)
''');
}

// 插入数据

Future insertDog(Map dog) async {
Database db = await instance.database;
return await db.insert('dogs', dog);
}

// 查询所有数据
Future>> queryAllDogs() async {
Database db = await instance.database;
return await db.query('dogs');
}

// 根据 ID 查询

Future?> queryDogById(int id) async {
Database db = await instance.database;
List> results = await db.query(
'dogs',
where: 'id = ?',
whereArgs: [id],
);
if (results.isNotEmpty) {
return results.first;
}
return null;
}

// 更新数据
Future updateDog(Map dog) async {
Database db = await instance.database;
int id = dog['id'];
return await db.update(
'dogs',
dog,
where: 'id = ?',
whereArgs: [id],
);
}

// 删除数据
Future deleteDog(int id) async {
Database db = await instance.database;
return await db.delete(
'dogs',
where: 'id = ?',
whereArgs: [id],
);
}

// 删除所有数据

Future deleteAllDogs() async{
Database db = await instance.database;
return await db.delete('dogs');
}
}
```

代码解释:

  • 单例模式: DatabaseHelper._privateConstructor()static final DatabaseHelper instance = ... 确保整个应用中只有一个 DatabaseHelper 实例。
  • get database: 懒加载数据库,只有在第一次需要访问数据库时才会创建。
  • _initDatabase():
    • getApplicationDocumentsDirectory(): 获取应用文档目录的路径。
    • join(documentsDirectory.path, 'my_database.db'): 构建数据库文件的完整路径。
    • openDatabase(): 打开数据库(如果不存在则创建)。
    • version: 数据库版本号。当数据库结构需要变更时(例如添加新表或修改表结构),应增加版本号。
    • onCreate: 创建数据库时执行的回调函数,通常用于创建表。
  • _onCreate(): 使用 SQL 语句创建名为 dogs 的表,包含 id (主键), name, 和 age 列。
  • CRUD Methods: 实现了插入 (insertDog), 查询所有 (queryAllDogs), 根据ID查询(queryDogById), 更新 (updateDog), 删除 (deleteDog) 和删除所有 (deleteAllDogs) 的基本数据库操作。 这些方法都使用了 sqflite 提供的 API。

3. 使用 DatabaseHelper

现在我们可以在 Flutter 应用中使用 DatabaseHelper 类来操作数据库了。

```dart
import 'package:flutter/material.dart';
import 'database_helper.dart'; // 导入 DatabaseHelper

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}

class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State {
final dbHelper = DatabaseHelper.instance; // 获取 DatabaseHelper 实例

List> _allDogs = [];
bool _isLoading = true;

@override
void initState() {
super.initState();
_loadDogs();
}

Future _loadDogs() async {
_allDogs = await dbHelper.queryAllDogs();
setState(() {
_isLoading = false;
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('SQLite Example'),
),
body: _isLoading ? Center(child:CircularProgressIndicator()) : ListView.builder(
itemCount: _allDogs.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_allDogs[index]['name']),
subtitle: Text('Age: ${_allDogs[index]['age']}'),
trailing: IconButton(icon: Icon(Icons.delete), onPressed: (){
_deleteDog(_allDogs[index]['id']);
},),
onTap: () {
_showUpdateDialog(_allDogs[index]);
},
);
},
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
_showInsertDialog();
},
),
);
}

// 显示插入数据的对话框

void _showInsertDialog() {
final nameController = TextEditingController();
final ageController = TextEditingController();

showDialog(
  context: context,
  builder: (context) {
    return AlertDialog(
      title: Text('Add Dog'),
      content: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          TextField(
            controller: nameController,
            decoration: InputDecoration(labelText: 'Name'),
          ),
          TextField(
            controller: ageController,
            decoration: InputDecoration(labelText: 'Age'),
            keyboardType: TextInputType.number,
          ),
        ],
      ),
      actions: [
        TextButton(
          child: Text('Cancel'),
          onPressed: () => Navigator.of(context).pop(),
        ),
        TextButton(
          child: Text('Add'),
          onPressed: () async {
            await _insertDog(nameController.text, int.parse(ageController.text));
            Navigator.of(context).pop();
          },
        ),
      ],
    );
  },
);

}

//插入
Future _insertDog(String name, int age) async {
Map dog = {
'name': name,
'age': age,
};
await dbHelper.insertDog(dog);
_loadDogs();
}

// 删除

Future _deleteDog(int id) async{
await dbHelper.deleteDog(id);
_loadDogs();
}

//显示更新对话框
void _showUpdateDialog(Map dog) {
final nameController = TextEditingController(text: dog['name']);
final ageController = TextEditingController(text: dog['age'].toString());
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Update Dog'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: nameController,
decoration: InputDecoration(labelText: 'Name'),
),
TextField(
controller: ageController,
decoration: InputDecoration(labelText: 'Age'),
keyboardType: TextInputType.number,
),
],
),
actions: [
TextButton(
child: Text('Cancel'),
onPressed: () => Navigator.of(context).pop(),
),
TextButton(
child: Text('Update'),
onPressed: () async {
await _updateDog(dog['id'] ,nameController.text, int.parse(ageController.text));
Navigator.of(context).pop();
},
),
],
);
},
);
}
//更新
Future _updateDog(int id, String name, int age) async{
Map updatedDog = {
'id' : id,
'name': name,
'age': age,
};
await dbHelper.updateDog(updatedDog);
_loadDogs();
}
}
```

代码解释:

  • 导入 DatabaseHelper: 引入我们之前创建的数据库帮助类。
  • 获取 DatabaseHelper 实例: final dbHelper = DatabaseHelper.instance;
  • initState: 初始化时加载数据。
  • _loadDogs: 使用dbHelper.queryAllDogs()查询所有数据, 并刷新UI。
  • ListView.builder: 将查询到的狗狗数据列表显示在 ListView 中。
  • _insertDog: 插入数据到数据库。
  • _deleteDog: 根据id删除数据。
  • _showInsertDialog: 显示对话框让用户输入新狗狗的信息。
  • FloatingActionButton: 点击按钮时,显示插入数据的对话框。
  • _showUpdateDialog: 显示更新对话框。
  • _updateDog: 更新数据库。
  • ListTile: 点击进入更新页面,长按删除。

4. 数据库升级 (Database Upgrade)

当你的应用需要更新数据库结构时(例如添加新表、修改表结构、删除表),你需要:

  1. 增加 _initDatabase() 方法中的 version 参数的值。
  2. onUpgrade 回调函数中编写升级逻辑。 onUpgrade 会在数据库版本号增加时被调用。

示例 (假设我们需要在 dogs 表中添加一个 breed 列):

```dart
// ... (DatabaseHelper 类中的其他代码) ...
Future _initDatabase() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, 'my_database.db');
return await openDatabase(
path,
version: 2, // 增加版本号
onCreate: _onCreate,
onUpgrade: _onUpgrade, // 添加 onUpgrade 回调
);
}

Future _onUpgrade(Database db, int oldVersion, int newVersion) async {
    if (oldVersion < 2) {
      await db.execute('ALTER TABLE dogs ADD COLUMN breed TEXT');
    }
}

// ... (DatabaseHelper 类中的其他代码) ...

```
解释:

  • version: 2: 我们将数据库版本号从 1 增加到 2。
  • onUpgrade: _onUpgrade: 指定 onUpgrade 回调函数。
  • _onUpgrade:
    • oldVersion: 旧的数据库版本号。
    • newVersion: 新的数据库版本号。
    • if (oldVersion < 2): 检查旧版本是否小于 2,如果是,则执行升级操作。
    • await db.execute('ALTER TABLE dogs ADD COLUMN breed TEXT');: 使用 ALTER TABLE 语句向 dogs 表添加 breed 列。
    • 通过if判断可以逐步升级, 比如从1升级到3, 会执行 oldVersion < 2oldVersion < 3 的代码

总结

本文详细介绍了在 Flutter 中使用 SQLite 的方法,包括添加依赖、创建数据库帮助类、执行 CRUD 操作以及数据库升级。通过这些步骤,你可以轻松地在 Flutter 应用中实现本地数据存储。 使用SQLite可以很好的持久化保存数据,并且可以结构化查询,对于较复杂的数据存储需求非常适合。

注意:
* 在实际应用中,建议对数据库操作进行错误处理(try-catch),并根据需要添加更详细的日志记录。
* getApplicationDocumentsDirectory() 返回的是应用专属的文档目录, 应用卸载后数据会被删除。 如果希望数据在应用卸载后仍然保留, 可以考虑使用其他路径, 但需要注意权限问题。
* 对于更复杂的数据库操作(例如事务、多表连接), 请参考 sqflite 插件的官方文档。
* 如果数据量非常大,可以考虑分批加载、分页显示等优化措施。

THE END