哈Ha! 我们提请您注意文章
“在Flutter中使用SQLite”的翻译 。

保存数据对用户非常重要,因为从网络加载相同的数据是不切实际的。 将它们保存在本地会更明智。
在本文中,我将演示如何在Flutter-e中使用SQLite做到这一点。
为什么选择SQLite?
SQLite是在移动设备上存储数据的最流行方法。 在本文中,我们将使用sqflite包使用SQLite。 Sqflite是将SQLite数据库连接到Flutter的最常用和最相关的库之一。
1.添加依赖项
在我们的项目中,打开
pubspec.yaml文件。 在依赖项下,添加最新版本的sqflite和path_provider。
dependencies: flutter: sdk: flutter sqflite: any path_provider: any
2.创建一个数据库客户端
现在创建一个新的Database.dart文件。 在其中创建一个单例。
为什么需要单身人士:我们使用这种模式来确保只有一个类实体,并为其提供全局入口点。
1.创建一个只能在此类内使用的私有构造函数。
class DBProvider { DBProvider._(); static final DBProvider db = DBProvider._(); }
2.设置数据库
下一步是创建数据库对象,并提供一个吸气剂,如果尚未创建该对象,我们将在其中创建数据库对象(延迟初始化)
static Database _database; Future<Database> get database async { if (_database != null) return _database; // if _database is null we instantiate it _database = await initDB(); return _database; }
如果没有对象分配给数据库,那么我们将调用initDB函数来创建数据库。 在此功能中,我们获得了保存数据库并创建所需表的路径
initDB() async { Directory documentsDirectory = await getApplicationDocumentsDirectory(); String path = join(documentsDirectory.path, "TestDB.db"); return await openDatabase(path, version: 1, onOpen: (db) { }, onCreate: (Database db, int version) async { await db.execute("CREATE TABLE Client (" "id INTEGER PRIMARY KEY," "first_name TEXT," "last_name TEXT," "blocked BIT" ")"); }); }
3.创建一个模型类
数据库内部的数据将转换为Dart Maps。 我们需要使用toMap和fromMap方法创建模型类。
要创建模型类,我将使用此
网站我们的模型:
/// ClientModel.dart import 'dart:convert'; Client clientFromJson(String str) { final jsonData = json.decode(str); return Client.fromJson(jsonData); } String clientToJson(Client data) { final dyn = data.toJson(); return json.encode(dyn); } class Client { int id; String firstName; String lastName; bool blocked; Client({ this.id, this.firstName, this.lastName, this.blocked, }); factory Client.fromJson(Map<String, dynamic> json) => new Client( id: json["id"], firstName: json["first_name"], lastName: json["last_name"], blocked: json["blocked"], ); Map<String, dynamic> toJson() => { "id": id, "first_name": firstName, "last_name": lastName, "blocked": blocked, }; }
4. CRUD操作
建立使用rawInsert:
newClient(Client newClient) async { final db = await database; var res = await db.rawInsert( "INSERT Into Client (id,first_name)" " VALUES (${newClient.id},${newClient.firstName})"); return res; }
使用插入:
newClient(Client newClient) async { final db = await database; var res = await db.insert("Client", newClient.toMap()); return res; }
使用大ID作为新ID的另一个示例
newClient(Client newClient) async { final db = await database; //get the biggest id in the table var table = await db.rawQuery("SELECT MAX(id)+1 as id FROM Client"); int id = table.first["id"]; //insert to the table using the new id var raw = await db.rawInsert( "INSERT Into Client (id,first_name,last_name,blocked)" " VALUES (?,?,?,?)", [id, newClient.firstName, newClient.lastName, newClient.blocked]); return raw; }
已读按编号获取客户
getClient(int id) async { final db = await database; var res =await db.query("Client", where: "id = ?", whereArgs: [id]); return res.isNotEmpty ? Client.fromMap(res.first) : Null ; }
获取所有有条件的客户
getAllClients() async { final db = await database; var res = await db.query("Client"); List<Client> list = res.isNotEmpty ? res.map((c) => Client.fromMap(c)).toList() : []; return list; }
仅获得被屏蔽的客户
getBlockedClients() async { final db = await database; var res = await db.rawQuery("SELECT * FROM Client WHERE blocked=1"); List<Client> list = res.isNotEmpty ? res.toList().map((c) => Client.fromMap(c)) : null; return list; }
更新资料更新现有客户端
updateClient(Client newClient) async { final db = await database; var res = await db.update("Client", newClient.toMap(), where: "id = ?", whereArgs: [newClient.id]); return res; }
客户端锁定/解锁
blockOrUnblock(Client client) async { final db = await database; Client blocked = Client( id: client.id, firstName: client.firstName, lastName: client.lastName, blocked: !client.blocked); var res = await db.update("Client", blocked.toMap(), where: "id = ?", whereArgs: [client.id]); return res; }
删掉删除一个客户
deleteClient(int id) async { final db = await database; db.delete("Client", where: "id = ?", whereArgs: [id]); }
删除所有客户端
deleteAll() async { final db = await database; db.rawDelete("Delete * from Client"); }
演示版
对于我们的演示,我们将创建一个显示数据库的简单应用程序。
首先我们组成屏幕
Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Flutter SQLite")), body: FutureBuilder<List<Client>>( future: DBProvider.db.getAllClients(), builder: (BuildContext context, AsyncSnapshot<List<Client>> snapshot) { if (snapshot.hasData) { return ListView.builder( itemCount: snapshot.data.length, itemBuilder: (BuildContext context, int index) { Client item = snapshot.data[index]; return ListTile( title: Text(item.lastName), leading: Text(item.id.toString()), trailing: Checkbox( onChanged: (bool value) { DBProvider.db.blockClient(item); setState(() {}); }, value: item.blocked, ), ); }, ); } else { return Center(child: CircularProgressIndicator()); } }, ), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: () async { Client rnd = testClients[math.Random().nextInt(testClients.length)]; await DBProvider.db.newClient(rnd); setState(() {}); }, ), ); }
注意事项:
1. FutureBuilder用于从数据库中获取数据
2. FAB初始化测试客户端
List<Client> testClients = [ Client(firstName: "Raouf", lastName: "Rahiche", blocked: false), Client(firstName: "Zaki", lastName: "oun", blocked: true), Client(firstName: "oussama", lastName: "ali", blocked: false), ];
3.没有数据时显示圆弧前进指示器。
4.当用户单击复选框时,客户端被阻止/解锁
现在,添加新功能非常容易,例如,如果我们想在轻扫客户端时删除客户端,只需将ListTile包装在Dismissible小部件中,如下所示:
return Dismissible( key: UniqueKey(), background: Container(color: Colors.red), onDismissed: (direction) { DBProvider.db.deleteClient(item.id); }, child: ListTile(...), );
重构以使用BLoC模式
我们在本文中做了很多工作,但是在实际应用程序中,在UI层中初始化状态并不是一个好主意。 将逻辑与UI分开。
Flutter中有很多模式,但我们将使用BLoC,因为它是最灵活的自定义方法。
创建BLoC class ClientsBloc { ClientsBloc() { getClients(); } final _clientController = StreamController<List<Client>>.broadcast(); get clients => _clientController.stream; dispose() { _clientController.close(); } getClients() async { _clientController.sink.add(await DBProvider.db.getAllClients()); } }
注意事项:
注意事项:
1. getClients异步地从数据库(Client表)接收数据。 每当需要更新表时都将使用此方法,因此值得将其放在构造函数的主体中。
2.我们创建了StreamController.broadcast,以便多次收听广播事件。 在我们的示例中,这并不重要,因为我们只听了一次,但是将来实现它会很好。
3.不要忘记关闭线程。 这样我们将阻止纪念馆。 在我们的示例中,我们使用StatefulWidget中的dispose方法关闭它们
现在看一下代码
blockUnblock(Client client) { DBProvider.db.blockOrUnblock(client); getClients(); } delete(int id) { DBProvider.db.deleteClient(id); getClients(); } add(Client client) { DBProvider.db.newClient(client); getClients(); }
最后是最终结果
来源可以在这里找到
-Github