Verwenden von SQLite in Flutter

Hallo Habr! Wir präsentieren Ihnen eine Übersetzung des Artikels „Verwenden von SQLite in Flutter“ .



Das Speichern von Daten ist für Benutzer sehr wichtig, da es unpraktisch ist, dieselben Daten aus dem Netzwerk zu laden. Es wäre klüger, sie lokal zu speichern.

In diesem Artikel werde ich zeigen, wie dies mit SQLite in Flutter-e gemacht wird

Warum SQLite?


SQLite ist die beliebteste Methode zum Speichern von Daten auf Mobilgeräten. In diesem Artikel verwenden wir das sqflite-Paket, um SQLite zu verwenden. Sqflite ist eine der am häufigsten verwendeten und relevantesten Bibliotheken zum Verbinden von SQLite-Datenbanken mit Flutter.

1. Fügen Sie Abhängigkeiten hinzu


Öffnen Sie in unserem Projekt die Datei pubspec.yaml . Fügen Sie unter den Abhängigkeiten die neueste Version von sqflite und path_provider hinzu.

dependencies: flutter: sdk: flutter sqflite: any path_provider: any 

2. Erstellen Sie einen DB-Client


Erstellen Sie nun eine neue Database.dart-Datei. Erstellen Sie darin einen Singleton.

Warum wir einen Singleton brauchen: Wir verwenden dieses Muster, um sicherzustellen, dass wir nur eine Klassenentität haben, und um einen globalen Einstiegspunkt darin bereitzustellen.

1. Erstellen Sie einen privaten Konstruktor, der nur innerhalb dieser Klasse verwendet werden kann.

 class DBProvider { DBProvider._(); static final DBProvider db = DBProvider._(); } 

2. Richten Sie die Datenbank ein

Der nächste Schritt besteht darin, das Datenbankobjekt zu erstellen und einen Getter bereitzustellen, in dem das Datenbankobjekt erstellt wird, wenn es noch nicht erstellt wurde (verzögerte Initialisierung).

 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; } 

Wenn der Datenbank kein Objekt zugewiesen ist, rufen wir die Funktion initDB auf, um die Datenbank zu erstellen. In dieser Funktion erhalten wir den Pfad zum Speichern der Datenbank und zum Erstellen der gewünschten Tabellen

 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. Erstellen Sie eine Modellklasse


Daten in der Datenbank werden in Dart Maps konvertiert. Wir müssen Modellklassen mit den Methoden toMap und fromMap erstellen.

Um Modellklassen zu erstellen, werde ich diese Site verwenden

Unser Modell:

 /// 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-Operationen


Erstellen

Verwenden von 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; } 

Mit einfügen:

 newClient(Client newClient) async { final db = await database; var res = await db.insert("Client", newClient.toMap()); return res; } 

Ein weiteres Beispiel für die Verwendung einer großen ID als neue 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; } 

Lesen Sie

Client nach ID abrufen

 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 ; } 

Holen Sie sich alle Kunden mit einer Bedingung

 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; } 

Erhalten Sie nur blockierte Kunden

 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; } 

Update

Aktualisieren Sie einen vorhandenen Client

 updateClient(Client newClient) async { final db = await database; var res = await db.update("Client", newClient.toMap(), where: "id = ?", whereArgs: [newClient.id]); return res; } 

Client sperren / entsperren

 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; } 

Löschen

Löschen Sie einen Client

 deleteClient(int id) async { final db = await database; db.delete("Client", where: "id = ?", whereArgs: [id]); } 

Alle Clients löschen

 deleteAll() async { final db = await database; db.rawDelete("Delete * from Client"); } 

Demo



Für unsere Demo erstellen wir eine einfache Anwendung, die unsere Datenbank anzeigt.

Zuerst machen wir den Bildschirm

 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(() {}); }, ), ); } 

Anmerkungen:

1. FutureBuilder wird verwendet, um Daten aus der Datenbank abzurufen

2. FAB zum Initialisieren von Testclients

 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. CircularProgressIndicator wird angezeigt, wenn keine Daten vorhanden sind.

4. Wenn der Benutzer auf die Kontrollkästchen klickt, wird der Client blockiert / entsperrt

Jetzt ist es sehr einfach, neue Funktionen hinzuzufügen. Wenn Sie beispielsweise den Client beim Wischen entfernen möchten, wickeln Sie die ListTile einfach in ein nicht mehr verwendbares Widget ein:

 return Dismissible( key: UniqueKey(), background: Container(color: Colors.red), onDismissed: (direction) { DBProvider.db.deleteClient(item.id); }, child: ListTile(...), ); 


Refactoring zur Verwendung eines BLoC-Musters


Wir haben in diesem Artikel viel getan, aber in realen Anwendungen ist es keine gute Idee, Zustände in der UI-Ebene zu initialisieren. Trennen Sie die Logik von der Benutzeroberfläche.

Es gibt viele Muster in Flutter, aber wir werden BLoC verwenden, da es für die Anpassung am flexibelsten ist.

Erstellen Sie 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()); } } 

Anmerkungen:
Anmerkungen:

1. getClients empfängt asynchron Daten aus der Datenbank (Client-Tabelle). Wir werden diese Methode verwenden, wenn wir die Tabelle aktualisieren müssen. Daher lohnt es sich, sie im Hauptteil des Konstruktors zu platzieren.

2. Wir haben StreamController.broadcast erstellt, um Broadcast-Ereignisse mehr als einmal anzuhören. In unserem Beispiel spielt dies keine Rolle, da wir sie nur einmal anhören, aber es wäre schön, dies für die Zukunft umzusetzen.

3. Vergessen Sie nicht, die Gewinde zu schließen. Auf diese Weise verhindern wir Denkmäler. In unserem Beispiel schließen wir sie mit der dispose-Methode in StatefulWidget

Schauen Sie sich jetzt den Code an

 blockUnblock(Client client) { DBProvider.db.blockOrUnblock(client); getClients(); } delete(int id) { DBProvider.db.deleteClient(id); getClients(); } add(Client client) { DBProvider.db.newClient(client); getClients(); } 

Und schließlich das Endergebnis


Quellen finden Sie hier - Github

Source: https://habr.com/ru/post/de435418/


All Articles