
老孟導讀:上一篇文章講解了 Android 和 iOS 的文件目錄系統,此篇文章講解如何使用 SQLite 保存數據。
歡迎大家投稿:http://laomengit.com/plan/Contribution.html
保存數據到本地是應用程序非常重要的功能之一,比如如下場景:一個新聞類或者博客類的應用程序,打開后進入首頁,如果本地沒有保存數據,則需要通過網絡獲取數據,在返回數據之前,用戶看到的是空白頁面,而如果本地保存了部分新聞,則顯示這部分數據,等待最新的數據返回時在刷新即可,對用戶體驗來說,明顯第二種體驗更佳。
SQLite 是目前最受歡迎的本地存儲框架之一,此篇文章將會介紹如何使用 SQLite 保存、查詢、刪除數據等。
SQLite pub地址:https://pub.flutter-io.cn/packages/sqflite
SQLite Github:https://github.com/tekartik/sqflite
SQLite 講解:https://www.sqlitetutorial.net/
如果您對數據庫和 SQL 語句相關知識有一點了解,下面的講解會比較容易理解,如果對數據庫和 SQL 語句相關知識一點不了解,那下面的講解理解起來可能會有一定的難度。
那是否需要先去學習數據庫和 SQL 語句相關知識?我覺得沒有必要,應用程序使用的數據庫和 SQL 語句相關知識都是非常基礎的,比如打開數據庫、創建表、SQL 查詢語句、更新語句、刪除語句等,這些都是固定的格式,固定語句記住即可。
添加依賴
SQLite 並不是 Flutter 系統自帶的,而是一個第三方插件,在項目的 pubspec.yaml 文件中添加依賴:
dependencies:
sqflite: ^1.3.1
path_provider: ^1.6.11
執行命令:
flutter pub get
使用 SQLite 創建數據庫的時候需要本地路徑做為參數,所以添加path_provider 插件獲取本地路徑。
單例模式創建 SQLite 訪問
使用 SQLite 並不是一定要使用單例模式,單例模式是為了保證整個應用程序僅有一個數據庫實例和全局訪問。
class DBProvider{
static final DBProvider _singleton = DBProvider._internal();
factory DBProvider() {
return _singleton;
}
DBProvider._internal();
}
初始化數據庫
import 'dart:io';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
class DBProvider {
static final DBProvider _singleton = DBProvider._internal();
factory DBProvider() {
return _singleton;
}
DBProvider._internal();
static Database _db;
Future<Database> get db async {
if (_db != null) {
return _db;
}
_db = await _initDB();
return _db;
}
Future<Database> _initDB() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, 'dbName');
return await openDatabase(path,
version: 1, onCreate: _onCreate, onUpgrade: _onUpgrade);
}
///
/// 創建Table
///
Future _onCreate(Database db, int version) async {}
///
/// 更新Table
///
Future _onUpgrade(Database db, int oldVersion, int newVersion) async {}
}
創建 Table,Table 代表一張表,下面創建一張用戶表,表列有 id(唯一標識)、name(姓名)、age(年齡)、sex(性別)。
///
/// 創建Table
///
Future _onCreate(Database db, int version) async {
return await db.execute("CREATE TABLE User ("
"id integer primary key AUTOINCREMENT,"
"name TEXT,"
"age TEXT,"
"sex integer"
")");
}
保存數據
先創建一個 User 的 Model 類,用於數據的保存:
class User {
int id;
String name;
int age;
int sex;
User({this.id, this.name, this.age, this.sex});
User.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
age = json['age'];
sex = json['sex'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['name'] = this.name;
data['age'] = this.age;
data['sex'] = this.sex;
return data;
}
}
保存數據:
Future saveData(User user) async {
var _db = await db;
return await _db.insert('User', user.toJson());
}
案例:輸入姓名、年齡、性別,點擊保存
class _AddUser extends StatefulWidget {
@override
__AddUserState createState() => __AddUserState();
}
class __AddUserState extends State<_AddUser> {
String _radioGroupValue = '0';
TextEditingController _nameController;
TextEditingController _ageController;
@override
void initState() {
super.initState();
_nameController = TextEditingController();
_ageController = TextEditingController();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('保存數據'),
),
body: Column(
children: <Widget>[
Row(
children: <Widget>[
Text('姓名:'),
Flexible(
child: TextField(
controller: _nameController,
),
),
],
),
Row(
children: <Widget>[
Text('年齡:'),
Flexible(
child: TextField(
controller: _ageController,
),
),
],
),
Row(
children: <Widget>[
Text('性別:'),
Flexible(
child: RadioListTile(
title: Text('男'),
value: '0',
groupValue: _radioGroupValue,
onChanged: (value) {
setState(() {
_radioGroupValue = value;
});
},
),
),
Flexible(
child: RadioListTile(
title: Text('女'),
value: '1',
groupValue: _radioGroupValue,
onChanged: (value) {
setState(() {
_radioGroupValue = value;
});
},
),
),
],
),
Builder(
builder: (context) {
return RaisedButton(
child: Text('保存'),
onPressed: () async {
var user = User(
name: '${_nameController.text}',
age: int.parse('${_ageController.text}'),
sex: int.parse('$_radioGroupValue'));
int result = await DBProvider().saveData(user);
if (result > 0) {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('保存數據成功,result:$result'),
));
} else {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('保存數據失敗,result:$result'),
));
}
},
);
},
)
],
),
);
}
}

使用 SQL 語句保存數據:
Future rawInsert(User user) async {
var _db = await db;
return await _db.rawInsert(
'INSERT Into User (name,age,sex) VALUES (?,?,?)',[user.name,user.age,user.sex]);
}
查詢數據
查詢全部數據:
Future<List<User>> findAll() async {
var _db = await db;
List<Map<String, dynamic>> result = await _db.query('User');
return result.isNotEmpty ? result.map((e) {
return User.fromJson(e);
}).toList():[];
}
將查詢的數據顯示在表格上:
class DatabaseDemo extends StatefulWidget {
@override
_DatabaseDemoState createState() => _DatabaseDemoState();
}
class _DatabaseDemoState extends State<DatabaseDemo> {
List<User> _list = [];
@override
void initState() {
super.initState();
_loadData();
}
_loadData() async {
_list = await DBProvider().findAll();
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Sqlite Demo'),
),
body: Column(
children: <Widget>[
Row(
children: <Widget>[
RaisedButton(
child: Text('查詢數據'),
onPressed: (){
_loadData();
},
),
RaisedButton(
child: Text('添加數據'),
onPressed: (){
Navigator.of(context).push(MaterialPageRoute(builder: (context){
return _AddUser();
}));
},
),
],
),
Table(
children: [
TableRow(children: [
TableCell(child: Text('id')),
TableCell(child: Text('姓名')),
TableCell(child: Text('年齡')),
TableCell(child: Text('性別')),
]),
..._list.map((user) {
return TableRow(children: [
TableCell(child: Text('${user.id}')),
TableCell(child: Text('${user.name}')),
TableCell(child: Text('${user.age}')),
TableCell(child: Text(user.sex == 0 ? '男' : '女')),
]);
}).toList()
],
)
],
),
);
}
}

根據條件查詢,比如查詢年齡為12的數據:
Future<List<User>> find(int age) async {
var _db = await db;
List<Map<String, dynamic>> result =
await _db.query('User', where: 'age = ?', whereArgs: [age]);
return result.isNotEmpty ? result.map((e) {
return User.fromJson(e);
}).toList():[];
}
使用:
_loadData() async {
_list = await DBProvider().find(12);
setState(() {});
}

更新數據
根據 User id 更新數據:
Future<int> update(User user) async {
var _db = await db;
return await _db
.update('User', user.toJson(), where: 'id = ?', whereArgs: [user.id]);
}
跳轉到修改數據頁面:
RaisedButton(
child: Text('修改第一行數據'),
onPressed: () {
if (_list.length > 1) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return _AddUser(
user: _list[0],
);
}));
}
},
),
保存修改的數據:
RaisedButton(
child: Text('保存'),
onPressed: () async {
var user = User(
name: '${_nameController.text}',
age: int.parse('${_ageController.text}'),
sex: int.parse('$_radioGroupValue'));
if (widget.user == null) {
_saveData(context,user);
} else {
_updateData(context,user);
}
},
)
_updateData(BuildContext context,User user) async {
user.id = widget.user.id;
int result = await DBProvider().update(user);
if (result > 0) {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('修改數據成功,result:$result'),
));
} else {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('修改數據失敗,result:$result'),
));
}
}

刪除數據
根據 id 刪除符合條件的數據:
Future<int> delete(int id) async {
var _db = await db;
return await _db.delete('User', where: 'id = ?', whereArgs: [id]);
}
刪除第一行數據,刪除成功后刷新數據:
RaisedButton(
child: Text('刪除第一行數據'),
onPressed: () async {
if (_list.length > 0) {
await DBProvider().delete(_list[0].id);
_loadData();
}
},
),

刪除全部數據:
Future<int> deleteAll() async {
var _db = await db;
return await _db.delete('User');
}
總結
我們介紹了 SQLite 的基本用法,數據的增刪改查是使用頻率最高的,SQLite 還有一些高級的查詢語句,比如分組、聯合查詢等,這些用到的頻率不多。
SQLite 創建成功后會在本地創建一個 db_name.db 的文件,文件的目錄就是初始化數據庫時設置的目錄。
交流
交流
老孟Flutter博客(330個控件用法+實戰入門系列文章):http://laomengit.com
歡迎加入Flutter交流群(微信:laomengit)、關注公眾號【老孟Flutter】:
![]() |
![]() |


