import 'package:flutter/material.dart'; import 'package:flutter/widgets.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 { // Isso identificará de forma exclusiva o formulário com o qual estamos // trabalhando e nos permitirá validar o formulário posteriormente final GlobalKey _formStateKey = GlobalKey(); // Armazenará a lista de cachorros lidos do banco. Future> dogs; // Armazenarão os dados de um objeto que será manipulado no app (ao criar ou atualizar). int _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. int dogIdForUpdate; // Armazena a conexão com o banco. DatabaseClient db; // 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(); // Cria conexão com o banco. db = DatabaseClient(); // 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. refreshDogList(); } // Método que atualiza a listagem de cachorros já cadastrados no banco. refreshDogList() { setState(() { dogs = db.getDogs(); }); } // Método que vai construir a interface do app. @override Widget build(BuildContext context) { return Scaffold( // Cria AppBar appBar: AppBar( title: Text('App Dog'), ), // Corpo do app. body: Column( children: [ // 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: [ Padding( padding: EdgeInsets.only(left: 10, right: 10, bottom: 10), // Adiciona TextForm para inserir o Id do cachorro. child: TextFormField( // Realiza validações de acordo com o valor preenchido. validator: (value) { if (value.isEmpty) { return 'Por favor insira o Id do cachorro'; } if (value.trim() == "") return "Somente espaço não é válido!"; return null; }, // Quando o formulário for enviado, salva o valor preenchido em _dogId. onSaved: (value) { _dogId = int.parse(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( // Ao ganhar foco cria uma sublinha o TextForm focusedBorder: new UnderlineInputBorder( borderSide: new BorderSide( color: Colors.cyan, 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.cyan, ), // Cor de fundo do TextForm fillColor: Colors.white, // Altera a cor da fonte do rótulo labelStyle: TextStyle( color: Colors.cyan, )), ), ), // 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: [ // 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: () { // 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(); // E chama o método "updateDog" da classe databaseClient db .updateDog(Dog( id: dogIdForUpdate, name: _dogName, age: _dogAge)) .then((data) { // Indica que a operação de atualização já foi realizada, // mudando a variável "isUpdate" setState(() { isUpdate = false; }); }); } } // 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(); // Chama o método "insertDog" da classe databaseClient db.insertDog( Dog(id: _dogId, name: _dogName, age: _dogAge)); } } // Após a atualização ou inserção, limpa os textos dos TextForms _dogIdController.text = ''; _dogNameController.text = ''; _dogAgeController.text = ''; // Chama método para recarregar a listagem de cachorros cadastrados. refreshDogList(); }, ), 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) { // 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 dogs) { return SingleChildScrollView( // Indica a direção de rolagem vertical. scrollDirection: Axis.vertical, child: SizedBox( // A largura da lista será a largura da tela do aparelho width: MediaQuery.of(context).size.width, // Monta DataTable com dados obtidos do banco (recebido aqui como parâmetro) // Monta cabeçalho child: DataTable( columns: [ DataColumn( label: Text('ID'), ), DataColumn( label: Text('NOME'), ), DataColumn( label: Text('IDADE'), ), DataColumn( label: Text('DELETAR'), ) ], // 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), // Ao clicar no botão chama o método deleteDog // da classe databaseClient onPressed: () { db.deleteDog(dog.id); refreshDogList(); }, ), ), ], ), ) .toList(), ), ), ); } }