Usando SQLite no Flutter

Olá Habr! Apresentamos a sua atenção uma tradução do artigo "Usando SQLite no Flutter" .



Salvar dados é muito importante para os usuários, pois é impraticável carregar os mesmos dados da rede. Seria mais sensato salvá-los localmente.

Neste artigo, demonstrarei como fazer isso usando SQLite no Flutter-e

Por que SQLite?


SQLite é a maneira mais popular de armazenar dados em dispositivos móveis. Neste artigo, usaremos o pacote sqflite para usar o SQLite. O Sqflite é uma das bibliotecas mais usadas e relevantes para conectar os bancos de dados SQLite ao Flutter.

1. Adicione dependências


Em nosso projeto, abra o arquivo pubspec.yaml . Sob as dependências, adicione a versão mais recente do sqflite e path_provider.

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

2. Crie um cliente de banco de dados


Agora crie um novo arquivo Database.dart. Nele, crie um singleton.

Por que precisamos de um singleton: usamos esse padrão para garantir que tenhamos apenas uma entidade de classe e fornecer um ponto de entrada global para ela.

1. Crie um construtor privado que só possa ser usado dentro desta classe.

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

2. Configure o banco de dados

O próximo passo é criar o objeto de banco de dados e fornecer um getter onde criaremos o objeto de banco de dados, se ele não tiver sido criado (inicialização lenta)

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

Se não houver um objeto atribuído ao banco de dados, chamaremos a função initDB para criar o banco de dados. Nesta função, obtemos o caminho para salvar o banco de dados e criar as tabelas desejadas

 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. Crie uma classe de modelo


Os dados dentro do banco de dados serão convertidos para o Dart Maps. Precisamos criar classes de modelo com os métodos toMap e fromMap.

Para criar classes de modelo, vou usar este site

Nosso modelo:

 /// 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. Operações CRUD


Criar

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

Usando inserção:

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

Outro exemplo usando um ID grande como um novo 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; } 

Ler

Obter cliente por ID

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

Obter todos os clientes com uma condição

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

Obtenha apenas clientes bloqueados

 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

Atualizar um cliente existente

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

Bloqueio / desbloqueio do cliente

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

Excluir

Excluir um cliente

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

Excluir todos os clientes

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

Demo



Para nossa demonstração, criaremos um aplicativo simples que exibe nosso banco de dados.

Primeiro compomos a tela

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

Notas:

1. FutureBuilder é usado para obter dados do banco de dados

2. FAB para inicializar clientes de teste

 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 é mostrado quando não há dados.

4. Quando o usuário clica nas caixas de seleção, o cliente é bloqueado / desbloqueado

Agora é muito fácil adicionar novos recursos, por exemplo, se queremos remover o cliente quando ele é deslizado, basta agrupar o ListTile em um widget Não permitido como este:

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


Refatorando para usar um padrão BLoC


Fizemos muito neste artigo, mas em aplicativos do mundo real, inicializar estados na camada da interface do usuário não é uma boa ideia. Separe a lógica da interface do usuário.

Existem muitos padrões no Flutter, mas usaremos o BLoC, pois ele é o mais flexível para personalização.

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

Notas:
Notas:

1. getClients recebe dados do banco de dados (tabela Client) de forma assíncrona. Usaremos esse método sempre que precisarmos atualizar a tabela; portanto, vale a pena colocá-lo no corpo do construtor.

2. Criamos StreamController.broadcast para ouvir eventos de transmissão mais de uma vez. No nosso exemplo, isso realmente não importa, pois apenas os ouvimos uma vez, mas seria bom implementá-lo para o futuro.

3. Não se esqueça de fechar os fios. Desta forma, impediremos memoriais. Em nosso exemplo, nós os fechamos usando o método dispose em StatefulWidget

Agora veja o código

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

E finalmente o resultado final


As fontes podem ser encontradas aqui - Github

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


All Articles