I'm developing a product catalog with a user registration and more. It's a simple app just to practice in Flutter. I'm using Getx for state management and Sqflite to persist local data (just to save the products and the users registered). But every time I'm gonna create a new user and save it in Sqflite, log in a user or recovery a password my UI gets freezed and it takes up to 10 seconds to get a response again. This not happens when creating, editing or deleting a product, it only happens when manipulating users. I dont know what else I can do.
Any help or suggestion will be appreciated.
Thanks in advance.
Controller to manipulate users
import 'package:nextar/database/database.dart';
import 'package:nextar/model/model_usuario.dart';
import 'package:get/get.dart';
import 'package:dbcrypt/dbcrypt.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ControllerCadastroUsuarios extends GetxController {
void onInit() {
super.onInit();
_database.database;
//verificarUsuarioLogado();
}
// Variável para chamar a instância do banco de dados
// para então, consequentemente, obter acesso aos comandos de SGBD
final DatabaseHelper _database = DatabaseHelper.instance;
// O método é responsável por cadastrar usuários corretamente no banco de dados.
void cadastrarUsuario(String nomeUsuario, String cpf, String senha,
String confirmarSenha) async {
// Verifica se o campo "Usuário" foi preenchido corretamente
List<Map<String, dynamic>> _verificarUsuario = await _database
.selectUsuarios(columns: ['cpf'], where: 'cpf = ?', whereArgs: [cpf]);
if (_verificarUsuario.length > 0) {
Get.snackbar('Divergência',
'Usuario já existente. Caso tenha esquecido de sua senha, recupere-a no link "Esqueceu sua senha?"');
return;
} else if (nomeUsuario.trim().isEmpty || nomeUsuario.length < 2) {
Get.snackbar('Divergência',
'O nome de usuário não pode estar vazio e precisa ser maior que dois caracteres. Por favor, revise seus dados.');
return;
}
// Se o anterior fora preenchido corretamente, verifica se o cpf digitado é válido
else if (cpf.trim().isEmpty || cpf.length < 14) {
Get.snackbar(
'Divergência', 'CPF inválido. Por favor, revise seus dados.');
return;
}
// Se o campo anterior fora preenchido corretamente, verifica se a senha digitada é válida
else if (senha.trim().isEmpty || senha.length < 6) {
Get.snackbar('Divergência',
'A senha não pode estar vazia e precisa ser maior que seis caracteres. Por favor, revise seus dados.');
return;
}
// Se o campo anterior fora preenchido corretamente, realiza uma comparação dos campos
// "Senha" e "Confirmar senha" se ambos são válidos
else if (senha != confirmarSenha) {
Get.snackbar('Divergência',
'As senhas não conferem. Por favor, revise seus dados.');
return;
}
// Se todos os campos anteriores foram preenchidos corretamente
// então é realizada uma busca no banco de dados para verificar a existêcnia do usuário
// o mesmo é feito para o CPF
else {
// Se as validações anteriores forem falsas, então o processo de criação de usuário é iniciado
final String _token =
DBCrypt().hashpw(senha, '\$2b\$10\$Ziu5wr71p1cY4H.hOeS/q.');
final Usuario _usuario =
Usuario(usuario: nomeUsuario, cpf: cpf, token: _token);
int _resposta = await _database.insertUsuario(row: _usuario.toMap());
print(_resposta);
// Se o retorno for maior que zero, então o usuário foi criado com sucesso
// caso contrário, ocorreu um erro na criação do usuário
if (_resposta > 0) {
Get.back();
Get.snackbar('Sucesso', 'Usuário cadastrado com sucesso!');
return;
} else {
Get.snackbar('Erro',
'Sentimos muito. Ocorreu um erro. Contate o suporte ou tente novamente mais tarde');
return;
}
}
}
// O método é responsável por permitir ou negar acessos ao sistema
// se caso o usuário não for reconhecido
void login(String usuario, String senha) async {
// Se os campos "Usuário" e "Senha" não forem preenchidos corretamente
// então é solicitado que os mesmos sejam preenchidos corretamente
if (usuario.trim().isEmpty || senha.trim().isEmpty) {
Get.snackbar(
'Divergência', 'Por favor, preencha os dados de login corretamente.');
return;
}
// Caso contrário é feita uma verificação no banco de dados acerca da
// existência do usuário
else {
SharedPreferences _prefs = await SharedPreferences.getInstance();
List<Map<String, dynamic>> _resposta = await _database.selectUsuarios(
columns: ['id_usuario', 'token', 'usuario'],
where: 'usuario = ?',
whereArgs: [usuario]);
// Se o usuário não existir, o sistema nega o acesso
if (_resposta.isEmpty) {
Get.snackbar(
'Divergência', 'Usuário inexistente. Por favor, revise seus dados');
return;
}
// Caso contrário, é feita uma verificação dos seus dados no banco de dados
else {
final bool _verificarToken =
DBCrypt().checkpw(senha, _resposta[0]['token']);
// Se o token verificado for verdadeiro, concede o acesso
if (_verificarToken) {
await _prefs.setString('token', _resposta[0]['token']);
await _prefs.setInt('idUsuario', _resposta[0]['id_usuario']);
await _prefs.setString('usuario', _resposta[0]['usuario']);
Get.toNamed('/principal');
return;
}
// Caso contrário, nega o mesmo
else {
Get.snackbar('Divergência',
'Dados divergentes. Por favor, verifique suas credenciais e tente novamente');
}
}
}
}
/*void verificarUsuarioLogado() async {
SharedPreferences _prefs = await SharedPreferences.getInstance();
var token = _prefs.getString('token');
if (token.isEmpty) {
Get.toNamed('/login');
} else {
Get.toNamed('/principal');
}
}*/
void deslogar() async {
SharedPreferences _prefs = await SharedPreferences.getInstance();
_prefs.clear();
Get.back();
}
void recuperarSenha(String cpf, String senha, String confirmarSenha) async {
if (cpf.trim().isEmpty || cpf.length < 14) {
Get.snackbar(
'Divergência', 'CPF inválido. Por favor, revise seus dados.');
} else if (senha.trim().isEmpty || senha.length < 6) {
Get.snackbar('Divergência',
'A senha não pode estar vazia e precisa ser maior que seis caracteres. Por favor, revise seus dados.');
} else if (senha != confirmarSenha) {
Get.snackbar('Divergência',
'As senhas não conferem. Por favor, revise seus dados.');
} else {
List<Map<String, dynamic>> _procurarCpf = await _database.selectUsuarios(
columns: ['cpf'], where: 'cpf = ?', whereArgs: [cpf]);
if (_procurarCpf.isEmpty) {
Get.snackbar('Divergência',
'Usuário não encontrado. Verifique seus dados e tente novamente');
} else {
final String token =
DBCrypt().hashpw(senha, '\$2b\$10\$Ziu5wr71p1cY4H.hOeS/q.');
int _senhaAtualizada =
await _database.updateUsuarios(row: {'cpf': cpf, 'token': token});
if (_senhaAtualizada > 0) {
Get.offAllNamed('/login');
Get.snackbar('Sucesso', 'Senha atualizada com sucesso!');
} else {
Get.snackbar('Erro',
'Sentimos muito. Ocorreu um erro. Contate o suporte ou tente novamente mais tarde');
}
}
}
}
}
Sqflite database
import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter/material.dart';
class DatabaseHelper {
static final _databaseName = "NextarSystemDB.db";
static final _databaseVersion = 2;
static final _tableUsuarios = 'Usuarios';
static final _columnIdUsuario = 'id_usuario';
static final _columnUsuario = 'usuario';
static final _columnCpf = 'cpf';
static final _columnToken = 'token';
static final _tableProdutos = 'Produtos';
static final _columnFkIdUsuario = 'id_usuario';
static final _columnIdProduto = 'id_produto';
static final _columnDescricaoProduto = 'descricaoProduto';
static final _columnPreco = 'preco';
static final _columnQuantidade = 'quantidade';
static final _columnCodigo = 'codigo';
static final _columnImagemProduto = 'imagem_produto';
static final _columnDataCriacao = 'data_criacao';
// Singleton
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
// tem somente uma referência ao banco de dados
static Database _database;
// instancia o db na primeira vez que for acessado
Future<Database> get database async {
if (_database != null) return _database;
_database = await _initDatabase();
return _database;
}
// abre o banco de dados e o cria se ele não existir
_initDatabase() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, _databaseName);
return await openDatabase(path,
version: _databaseVersion, onCreate: _onCreateTable);
}
// Código SQL para criar o banco de dados e a tabela
Future _onCreateTable(Database db, int version) async {
await db.execute('''
CREATE TABLE $_tableUsuarios (
$_columnIdUsuario INTEGER PRIMARY KEY NOT NULL,
$_columnUsuario VARCHAR(30) NOT NULL,
$_columnCpf VARCHAR(14) UNIQUE NOT NULL,
$_columnToken VARCHAR(100) NOT NULL
)
''');
await db.execute('''
CREATE TABLE $_tableProdutos (
$_columnIdProduto INTEGER PRIMARY KEY NOT NULL,
$_columnFkIdUsuario INTEGER,
$_columnDescricaoProduto VARCHAR(60) NOT NULL,
$_columnPreco DECIMAL (9, 2),
$_columnQuantidade INTEGER,
$_columnCodigo VARCHAR(40) NOT NULL,
$_columnImagemProduto BLOB,
$_columnDataCriacao VARCHAR(20) NOT NULL,
FOREIGN KEY (id_usuario) REFERENCES Usuarios (id_usuario) ON DELETE NO ACTION ON UPDATE NO ACTION
)
''');
}
// métodos Helper
//----------------------------------------------------
// Insere uma linha no banco de dados onde cada chave
// no Map é um nome de coluna e o valor é o valor da coluna.
// O valor de retorno é o id da linha inserida.
Future<int> insertUsuario({@required Map<String, dynamic> row}) async {
Database db = await instance.database;
return await db.rawInsert('''INSERT INTO $_tableUsuarios(
$_columnUsuario,
$_columnCpf,
$_columnToken)
VALUES(?, ?, ?)''',
[row[_columnUsuario], row[_columnCpf], row[_columnToken]]);
}
Future<int> insertProduto({@required Map<String, dynamic> row}) async {
Database db = await instance.database;
return await db.rawInsert('''INSERT INTO $_tableProdutos(
$_columnFkIdUsuario,
$_columnDescricaoProduto,
$_columnPreco,
$_columnQuantidade,
$_columnCodigo,
$_columnDataCriacao,
$_columnImagemProduto)
VALUES(?, ?, ?, ?, ?, ?, ?)''', [
row[_columnIdUsuario],
row[_columnDescricaoProduto],
row[_columnPreco],
row[_columnQuantidade],
row[_columnCodigo],
row[_columnDataCriacao],
row[_columnImagemProduto]
]);
}
Future<int> deleteProduto({@required String codigoProduto}) async {
Database db = await instance.database;
return await db.delete(_tableProdutos,
where: 'codigo = ?', whereArgs: [codigoProduto]);
}
// seleciona o usuário desejado
// em columns você insere um array de strings com as colunas desejadas
// where você define a condição para a clausula where
// em whereArgs você insere os parâmetros de filtro para os dados desejados
// o valor de retorno é uma lista de maps com os valores solicitados
Future<List<Map<String, dynamic>>> selectUsuarios(
{@required List<String> columns,
String where,
List<String> whereArgs}) async {
Database db = await instance.database;
return await db.query(_tableUsuarios,
columns: columns, where: where, whereArgs: whereArgs);
}
Future<List<Map<String, dynamic>>> selectProdutos(
{@required List<String> columns,
String where,
List<String> whereArgs}) async {
Database db = await instance.database;
return await db.query(_tableProdutos,
columns: columns, where: where, whereArgs: whereArgs);
}
// Todas as linhas são retornadas como uma lista de mapas, onde cada mapa é
// uma lista de valores-chave de colunas.
Future<List<Map<String, dynamic>>> queryAllRowsUsuarios() async {
Database db = await instance.database;
return await db.query(_tableUsuarios);
}
Future<List<Map<String, dynamic>>> queryAllRowsProdutos(
{@required int idUsuario}) async {
Database db = await instance.database;
return await db
.query(_tableProdutos, where: 'id_usuario = ?', whereArgs: [idUsuario]);
}
Future<List<Map<String, dynamic>>> queryAllRowsProdutosOrdemAlfabetica(
{@required int idUsuario}) async {
Database db = await instance.database;
return await db.query(_tableProdutos,
where: 'id_usuario = ?',
whereArgs: [idUsuario],
orderBy: _columnDescricaoProduto);
}
Future<List<Map<String, dynamic>>> queryAllRowsProdutosPreco(
{@required int idUsuario}) async {
Database db = await instance.database;
return await db.query(_tableProdutos,
where: 'id_usuario = ?', whereArgs: [idUsuario], orderBy: _columnPreco);
}
Future<List<Map<String, dynamic>>> queryAllRowsProdutosData(
{@required int idUsuario}) async {
Database db = await instance.database;
return await db.query(_tableProdutos,
where: 'id_usuario = ?',
whereArgs: [idUsuario],
orderBy: _columnDataCriacao);
}
// Assumimos aqui que a coluna cpf no mapa está definida. Os outros
// valores das colunas serão usados para atualizar a linha.
Future<int> updateUsuarios({@required Map<String, dynamic> row}) async {
Database db = await instance.database;
String cpf = row[_columnCpf];
return await db.update(_tableUsuarios, row,
where: '$_columnCpf = ?', whereArgs: [cpf]);
}
Future<int> updateProdutos({@required Map<String, dynamic> row}) async {
Database db = await instance.database;
int idProduto = row[_columnIdProduto];
return await db.update(_tableProdutos, row,
where: '$_columnIdProduto = ?', whereArgs: [idProduto]);
}
}
OBS: I'm calling the ControllerUsuarios (Get.put()) in main.dart and this behavior only happens in build release