老孟導讀:上一篇文章講解了 Android 和 iOS 的文件目錄系統,此篇文章講解如何使用 SQLite 保存數據。
歡迎你們投稿:http://laomengit.com/plan/Contribution.htmlhtml
保存數據到本地是應用程序很是重要的功能之一,好比以下場景:一個新聞類或者博客類的應用程序,打開後進入首頁,若是本地沒有保存數據,則須要經過網絡獲取數據,在返回數據以前,用戶看到的是空白頁面,而若是本地保存了部分新聞,則顯示這部分數據,等待最新的數據返回時在刷新便可,對用戶體驗來講,明顯第二種體驗更佳。git
SQLite 是目前最受歡迎的本地存儲框架之一,此篇文章將會介紹如何使用 SQLite 保存、查詢、刪除數據等。github
SQLite pub地址:https://pub.flutter-io.cn/packages/sqflitesql
SQLite Github:https://github.com/tekartik/sqflite數據庫
SQLite 講解:https://www.sqlitetutorial.net/json
若是您對數據庫和 SQL 語句相關知識有一點了解,下面的講解會比較容易理解,若是對數據庫和 SQL 語句相關知識一點不瞭解,那下面的講解理解起來可能會有必定的難度。微信
那是否須要先去學習數據庫和 SQL 語句相關知識?我以爲沒有必要,應用程序使用的數據庫和 SQL 語句相關知識都是很是基礎的,好比打開數據庫、建立表、SQL 查詢語句、更新語句、刪除語句等,這些都是固定的格式,固定語句記住便可。網絡
SQLite 並非 Flutter 系統自帶的,而是一個第三方插件,在項目的 pubspec.yaml
文件中添加依賴:app
dependencies: sqflite: ^1.3.1 path_provider: ^1.6.11
執行命令:框架
flutter pub get
使用 SQLite 建立數據庫的時候須要本地路徑作爲參數,因此添加path_provider
插件獲取本地路徑。
使用 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】: