Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Importa bibliotecas.
- import 'package:flutter/material.dart';
- import 'package:flutter/widgets.dart';
- import 'controls/apiClient.dart';
- import 'controls/databaseClient.dart';
- import 'models/dog.dart';
- void main() => runApp(MyApp());
- class MyApp extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return MaterialApp(
- theme: ThemeData(
- primarySwatch: Colors.cyan,
- ),
- home: DogPage(),
- );
- }
- }
- class DogPage extends StatefulWidget {
- @override
- _DogPageState createState() => _DogPageState();
- }
- class _DogPageState extends State<DogPage> {
- // Isso identificará de forma exclusiva o formulário com o qual estamos
- // trabalhando e nos permitirá validar o formulário posteriormente
- final GlobalKey<FormState> _formStateKey = GlobalKey<FormState>();
- // Utilizado para armazenar o context e assim utilizarmos os snackbars
- // em qualquer lugar da aplicação.
- final _scaffoldKey = GlobalKey<ScaffoldState>();
- // Armazenará a lista de cachorros lidos do banco.
- Future<List<Dog>> dogs;
- // Lista auxiliares que serão utilizadas para armazenar dados da API e do BD.
- List<Dog> dogsFromApi;
- List<Dog> dogsFromDb;
- // Armazenarão os dados de um objeto que será manipulado no app (ao criar ou atualizar).
- String _dogId;
- String _dogName;
- int _dogAge;
- // Indica se a operação é de update (true) ou insert (false).
- bool isUpdate = false;
- // Armazena o id do cachorro que será atualizado em uma operação de update.
- String dogIdForUpdate;
- // Armazena objeto para manipular o banco.
- DatabaseClient db;
- // Armazena objeto para manipular a API.
- ApiClient api;
- // Controladores que vão manipular os textos dos TextEdits.
- final _dogIdController = TextEditingController();
- final _dogNameController = TextEditingController();
- final _dogAgeController = TextEditingController();
- // Executada ao inicializar o aplicativo.
- @override
- void initState() {
- super.initState();
- // Instancia objeto da classe DatabaseClient.
- db = DatabaseClient();
- // Instancia objeto da classe ApiClient.
- api = ApiClient();
- // Chama método para abrir o banco.
- openDatabase();
- }
- // Método que chama a função open da classe "databaseClient"
- openDatabase() async {
- await db.open();
- // Chama método para atualizar a listagem de cachorros já cadastrados no banco.
- await refreshDogList();
- }
- // Método que atualiza a listagem de cachorros já cadastrados no banco.
- refreshDogList() {
- setState(() {
- dogs = db.getDogs();
- });
- }
- // Método responsável por comandar o envio de um objeto Dog para a API.
- Future<Dog> uploadOnApi(dog) async {
- // Tenta fazer a operação.
- try {
- Dog newDog = await api.insertDog(dog);
- // Mostra snackbar de sucesso.
- final snackBar = SnackBar(
- content: Text('Dados enviados com sucesso para a API!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.green,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- // Ao receber o dado, a API retorna o objeto que foi salvo nela,
- // incluindo o id que ela utilizou (auto incremento), assim poderemos pegar
- // este objeto e salvar posteriormente no BD.
- return newDog;
- }
- // Caso tenha erro ao realizar a operação.
- catch (error) {
- // Mostra snackbar de erro.
- final snackBar = SnackBar(
- content: Text('Erro ao enviar dado para a API!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.red,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- return null;
- }
- }
- // Método responsável por comandar o envio de um objeto Dog para o BD.
- uploadOnDb(dog) async {
- // Tenta fazer a operação.
- try {
- await db.insertDog(dog);
- final snackBar = SnackBar(
- content: Text('Dados enviados com sucesso para a BD!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.green,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- }
- // Caso tenha erro ao realizar a operação.
- catch (error) {
- // Mostra snackbar de erro.
- final snackBar = SnackBar(
- content: Text('Erro ao enviar dado para o BD!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.red,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- }
- }
- // Método responsável por comandar a remoção de um objeto Dog na API.
- deleteOnApi(Dog dog) async {
- // Tenta fazer a operação.
- try {
- await api.deleteDog(dog.id);
- final snackBar = SnackBar(
- content: Text('Dado removido com sucesso da API!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.green,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- }
- // Caso tenha erro ao realizar a operação.
- catch (error) {
- // Mostra snackbar de erro.
- final snackBar = SnackBar(
- content: Text('Erro ao remover dado da API!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.red,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- }
- }
- // Método responsável por comandar a remoção de um objeto Dog no BD.
- deleteOnDb(Dog dog) async {
- // Tenta fazer a operação.
- try {
- await db.deleteDog(dog.id);
- final snackBar = SnackBar(
- content: Text('Dado removido com sucesso do BD!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.green,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- }
- // Caso tenha erro ao realizar a operação.
- catch (error) {
- // Mostra snackbar de erro.
- final snackBar = SnackBar(
- content: Text('Erro ao remover dado do BD!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.red,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- }
- }
- // Método responsável por comandar a atualização de um objeto Dog na API.
- Future<Dog> updateOnApi(Dog dog) async {
- // Tenta fazer a operação.
- try {
- Dog newDog = await api.updateDog(dog);
- // Mostra snackbar de sucesso.
- final snackBar = SnackBar(
- content: Text('Dado atualizado com sucesso da API!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.green,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- // Ao receber o dado, a API retorna o objeto que foi atualizado nela,
- // incluindo o id que ela utilizou (auto incremento), assim poderemos pegar
- // este objeto e atualizar posteriormente também no BD.
- return newDog;
- }
- // Caso tenha erro ao realizar a operação.
- catch (error) {
- // Mostra snackbar de erro.
- final snackBar = SnackBar(
- content: Text('Erro ao atualizar dado na API!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.red,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- return null;
- }
- }
- // Método responsável por comandar a atualização de um objeto Dog no BD.
- updateOnDb(Dog dog) async {
- // Tenta fazer a operação.
- try {
- await db.updateDog(dog);
- final snackBar = SnackBar(
- content: Text('Dado atualizado com sucesso do BD!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.green,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- }
- // Caso tenha erro ao realizar a operação.
- catch (error) {
- // Mostra snackbar de erro.
- final snackBar = SnackBar(
- content: Text('Erro ao atualizar dado no BD!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.red,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- }
- }
- // Método responsável por solicitar o download de todos os dados da API e então
- // atualizar a base de dados local.
- downloadFromApi() async {
- // Variável utilizada para indicar se ocorreu erros nesta operação.
- var errorDownload = false;
- // Tenta fazer a leitura dos dados da API.
- try {
- dogsFromApi = await api.getDogs();
- // Mostra snackbar de sucesso.
- final snackBar = SnackBar(
- content: Text('Dados lidos com sucesso na API!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.green,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- }
- // Caso dê erro (ex: url incorreta ou sem internet)
- catch (erro) {
- // Indica que ocorreu um erro na variável.
- errorDownload = true;
- // Mostra snackbar de erro.
- final snackBar = SnackBar(
- content: Text('Erro ao comunicar com a API!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.red,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- }
- // Se a operação de busca de todos os dados da API foi feita sem erros.
- if (errorDownload == false) {
- // Solicita remoção de todos os dados do BD.
- await db.deleteAllDogs();
- // Solicita escrita de todos os dados lidos na API no BD.
- await db.insertDogs(dogsFromApi);
- // Atualiza lista Dogs para exibição no grid.
- refreshDogList();
- // Mostra snackbar de sucesso.
- final snackBar = SnackBar(
- content: Text('Dados armazenados com sucesso no BD!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.green,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- }
- }
- // Método que vai construir a interface do app.
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- key: _scaffoldKey,
- // Cria AppBar
- appBar: AppBar(title: Text('App Dog'), actions: <Widget>[
- // Adiciona botão para download dos dados da API.
- Padding(
- padding: EdgeInsets.only(right: 20.0),
- child: GestureDetector(
- onTap: () {
- // Ao clicar chama método para obtenção de todos os dados da API.
- downloadFromApi();
- },
- child: Icon(Icons.cloud_download),
- )),
- ]),
- // Corpo do app.
- body: Column(
- children: <Widget>[
- // Cria um formulário.
- Form(
- // Atribui a chave para identificar este formulário.
- key: _formStateKey,
- // Indica que a validação dos campos deve ser verificada automaticamente.
- autovalidate: true,
- // Adiciona Padding
- child: Column(
- children: <Widget>[
- Padding(
- padding: EdgeInsets.only(left: 10, right: 10, bottom: 10),
- // Adiciona TextForm para inserir o Id do cachorro.
- child: TextFormField(
- enabled: false,
- // Quando o formulário for enviado, salva o valor preenchido em _dogId.
- onSaved: (value) {
- _dogId = value;
- },
- // Indica quem é o controlador deste TextForm.
- controller: _dogIdController,
- // Indica que o teclado padrão deve ser numérico.
- keyboardType: TextInputType.number,
- // Personaliza o TextForm
- decoration: InputDecoration(
- disabledBorder: new UnderlineInputBorder(
- borderSide: new BorderSide(
- color: Colors.grey,
- width: 2,
- style: BorderStyle.solid)),
- // Rótulo do TextForm
- labelText: "Id do cachorro",
- // Ícone que será exibido ao lado do TextForm
- icon: Icon(
- Icons.vpn_key,
- color: Colors.grey,
- ),
- // Cor de fundo do TextForm
- fillColor: Colors.white,
- // Altera a cor da fonte do rótulo
- labelStyle: TextStyle(
- color: Colors.grey,
- )),
- ),
- ),
- // Repete passos anteriores, porém agora, para o TextForm do nome.
- Padding(
- padding: EdgeInsets.only(left: 10, right: 10, bottom: 10),
- child: TextFormField(
- validator: (value) {
- if (value.isEmpty) {
- return 'Por favor insira o nome do cachorro';
- }
- if (value.trim() == "")
- return "Somente espaço não é válido!";
- return null;
- },
- onSaved: (value) {
- _dogName = value;
- },
- controller: _dogNameController,
- decoration: InputDecoration(
- focusedBorder: new UnderlineInputBorder(
- borderSide: new BorderSide(
- color: Colors.cyan,
- width: 2,
- style: BorderStyle.solid)),
- labelText: "Nome do cachorro",
- icon: Icon(
- Icons.pets,
- color: Colors.cyan,
- ),
- fillColor: Colors.white,
- labelStyle: TextStyle(
- color: Colors.cyan,
- )),
- ),
- ),
- // Repete passos anteriores, porém agora, para o TextForm da idade.
- Padding(
- padding: EdgeInsets.only(left: 10, right: 10, bottom: 10),
- child: TextFormField(
- validator: (value) {
- if (value.isEmpty) {
- return 'Por favor insira a idade do cachorro';
- }
- if (value.trim() == "")
- return "Somente espaço não é válido!";
- return null;
- },
- onSaved: (value) {
- _dogAge = int.parse(value);
- },
- controller: _dogAgeController,
- keyboardType: TextInputType.number,
- decoration: InputDecoration(
- focusedBorder: new UnderlineInputBorder(
- borderSide: new BorderSide(
- color: Colors.cyan,
- width: 2,
- style: BorderStyle.solid)),
- labelText: "Idade do cachorro",
- icon: Icon(
- Icons.calendar_today,
- color: Colors.cyan,
- ),
- fillColor: Colors.white,
- labelStyle: TextStyle(
- color: Colors.cyan,
- )),
- ),
- ),
- ],
- ),
- ),
- // Após o fórmulário, organiza na horizontal os botões de ação.
- Row(
- // Centraliza os botões.
- mainAxisAlignment: MainAxisAlignment.center,
- // Adiciona botões.
- children: <Widget>[
- // Botão de dupla função (Atualizar ou Adicionar).
- RaisedButton(
- color: Colors.cyan,
- // Se a operação for de atualização mostra "ATUALIZAR", caso
- // contrário, mostra "ADICIONAR".
- child: Text(
- (isUpdate ? 'ATUALIZAR' : 'ADICIONAR'),
- style: TextStyle(color: Colors.white),
- ),
- // Programa função do clique no botão
- onPressed: () async {
- FocusScope.of(context).requestFocus(new FocusNode());
- // Se a operação for de atualização
- if (isUpdate) {
- // Verifica se os dados dos TextForms estão válidos.
- if (_formStateKey.currentState.validate()) {
- // Se sim, salva os dados nas variáveis.
- _formStateKey.currentState.save();
- // Tenta fazer atualização dos dados.
- try {
- // Cria objeto Dog com os dados dos campos de formulário.
- Dog dog = Dog(
- id: dogIdForUpdate, name: _dogName, age: _dogAge);
- // Solicita primeiro a atualização na API, ela então retornará
- // um objeto com os dados atualizados nela, em seguida
- // chama método para atualizar este objeto no banco
- await updateOnDb(await updateOnApi(dog));
- // Solicita que a lista Dogs seja atualizada (para o grid).
- refreshDogList();
- // Indica que a atualização acabou (assim os botões voltam ao estado de adicionar).
- setState(() {
- isUpdate = false;
- });
- }
- // Caso encontre algum erro nas funções acima.
- catch (error) {
- print(error);
- final snackBar = SnackBar(
- content: Text('Erro ao comunicar com a API!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.red,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- }
- }
- }
- // Caso a operação a ser executada seja a de inserção de um novo dado.
- else {
- // Verifica se os dados dos TextForms estão válidos.
- if (_formStateKey.currentState.validate()) {
- // Se sim, salva os dados nas variáveis.
- _formStateKey.currentState.save();
- // Tenta fazer inserção dos dados.
- try {
- // Cria objeto Dog com os dados dos campos de formulário.
- Dog dog = Dog(id: _dogId, name: _dogName, age: _dogAge);
- // Solicita primeiro a inserção na API, ela então retornará
- // um objeto com os dados inseridos nela, em seguida
- // chama método para inserir este objeto no banco.
- // Isso é necessário visto que o id é auto_increment na API,
- // desta maneira o objeto retornado pela API já tem o id correto.
- await uploadOnDb(await uploadOnApi(dog));
- // Solicita que a lista Dogs seja atualizada (para o grid).
- refreshDogList();
- }
- // Caso encontre algum erro nas funções acima.
- catch (error) {
- final snackBar = SnackBar(
- content: Text('Erro ao comunicar com a API!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.red,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- }
- }
- }
- // Após a atualização ou inserção, limpa os textos dos TextForms
- _dogIdController.text = '';
- _dogNameController.text = '';
- _dogAgeController.text = '';
- },
- ),
- Padding(
- padding: EdgeInsets.all(10),
- ),
- // Botão com dupla função: cancelar a atualização ou limpar os campos
- RaisedButton(
- color: Colors.red,
- // Se a operação for de atualização mostra "CANCELAR ATUALIZAÇÃO",
- // caso contrário, mostra "CANCELAR".
- child: Text(
- (isUpdate ? 'CANCELAR ATUALIZAÇÃO' : 'LIMPAR'),
- style: TextStyle(color: Colors.white),
- ),
- // Programa o que ocorrerá quando o botão for clicado
- onPressed: () {
- // Em ambas as situações limpa os dados dos TextForms
- _dogIdController.text = '';
- _dogNameController.text = '';
- _dogAgeController.text = '';
- setState(() {
- // Indica que não está ocorrendo mais uma atualização
- isUpdate = false;
- // Limpa a variável que armazenaria o id a ser atualizado
- dogIdForUpdate = null;
- });
- },
- ),
- ],
- ),
- const Divider(
- height: 20.0,
- ),
- // Componente que exibirá a listagem com os cachorros cadastrados.
- Expanded(
- // Para saber +: https://tinyurl.com/y44rl6ce
- child: FutureBuilder(
- future: dogs,
- builder: (context, snapshot) {
- print(snapshot.connectionState);
- // Caso não exista dados a serem exibidos, mostra uma mensagem
- if (snapshot.data == null || snapshot.data.length == 0) {
- return Text('Sem dados para exibir');
- }
- // Se o banco tiver retornado dados para exibir, ou seja, dados
- // cadastrados, chama método para gerar a lista de cachorros e
- // e posteriormente exibir tal lista na tela.
- else if (snapshot.hasData) {
- return generateList(snapshot.data);
- }
- // Retorna um widget circular que será apresentado enquanto a
- // operação de recuperação de dados estiver sendo processada.
- return CircularProgressIndicator();
- },
- ),
- ),
- ],
- ),
- );
- }
- // Método que retorna um widget com a lista de cachorros cadastrados no banco.
- SingleChildScrollView generateList(List<Dog> dogs) {
- return SingleChildScrollView(
- scrollDirection: Axis.vertical,
- child: SingleChildScrollView(
- scrollDirection: Axis.horizontal,
- child: DataTable(
- columns: [
- DataColumn(
- label: Text('ID', style: TextStyle(fontStyle: FontStyle.italic)),
- ),
- DataColumn(
- label:
- Text('NOME', style: TextStyle(fontStyle: FontStyle.italic)),
- ),
- DataColumn(
- label:
- Text('IDADE', style: TextStyle(fontStyle: FontStyle.italic)),
- ),
- DataColumn(
- label: Text('DELETAR',
- style: TextStyle(fontStyle: FontStyle.italic)),
- )
- ],
- // Monta linhas
- rows: dogs
- .map(
- (dog) => DataRow(
- cells: [
- // Célula que exibirá o id do cachorro.
- DataCell(
- Text(dog.id.toString()),
- // Quando o usuário clicar nesta célula ativa a operação
- // de atualização.
- onTap: () {
- setState(() {
- // Indica que a operação de atualização está ativada
- isUpdate = true;
- // Salva o id do objeto apresentado na linha selecionada
- dogIdForUpdate = dog.id;
- });
- // Preenche, com os dados do cachorro selecionado, os TextForms
- _dogIdController.text = dog.id.toString();
- _dogNameController.text = dog.name;
- _dogAgeController.text = dog.age.toString();
- },
- ),
- // Célula que exibirá o nome do cachorro.
- DataCell(
- Text(dog.name),
- onTap: () {
- setState(() {
- isUpdate = true;
- dogIdForUpdate = dog.id;
- });
- _dogIdController.text = dog.id.toString();
- _dogNameController.text = dog.name;
- _dogAgeController.text = dog.age.toString();
- },
- ),
- // Célula que exibirá a idade do cachorro.
- DataCell(
- Text(dog.age.toString()),
- onTap: () {
- setState(() {
- isUpdate = true;
- dogIdForUpdate = dog.id;
- });
- _dogIdController.text = dog.id.toString();
- _dogNameController.text = dog.name;
- _dogAgeController.text = dog.age.toString();
- },
- ),
- // Célula que exibirá o botão para deletar uma linha do banco
- DataCell(
- IconButton(
- icon: Icon(Icons.delete),
- // Coordena a remoção de um dado do grid, API e BD.
- onPressed: () {
- // Tenta fazer deleção dos dados.
- try {
- // Solicita que o objeto seja removido da API.
- deleteOnApi(dog);
- // Solicita que o objeto seja removido do BD.
- deleteOnDb(dog);
- // Solicita que a lista Dogs seja atualizada (para o grid).
- refreshDogList();
- }
- // Caso encontre algum erro nas funções acima.
- catch (error) {
- final snackBar = SnackBar(
- content: Text('Erro ao comunicar com a API!'),
- duration: Duration(milliseconds: 500),
- backgroundColor: Colors.red,
- );
- _scaffoldKey.currentState.showSnackBar(snackBar);
- }
- },
- ),
- ),
- ],
- ),
- )
- .toList(),
- ),
- ),
- );
- }
- }
Add Comment
Please, Sign In to add comment