%%| label: mindmap-projeto
%%| fig-cap: Projeto Integrador
%%| fig-width: 11
mindmap
root((Projeto Integrador))
Metodologias Ativas
Sala de Aula Invertida
Cultura Maker
Aprendizagem Baseada em Projeto
Aprendizagem Baseada em Times
Design Thinking
Competências Técnicas
Flutter e Dart
Backend AWS
Banco de Dados
Segurança e Auth
Testes e Publicação
Competências Transversais
Trabalho em Equipe
Comunicação Técnica
Resolução de Problemas
Autonomia
Entregas
Incrementos Semanais
Diário de Desenvolvimento
Entrega Parcial Módulo 10
Entrega Final Módulo 15
Projeto Integrador
Ao longo desta disciplina, você não vai apenas estudar conceitos isolados sobre desenvolvimento mobile. Você vai construir uma aplicação real, funcional e completa, do zero até a publicação. Esse é o Projeto Integrador: a espinha dorsal da disciplina, o lugar onde tudo o que você aprender nas aulas se transforma em algo concreto e tangível.
Este documento descreve em detalhes como o Projeto Integrador funciona, como o seu grupo deve se organizar, o que é esperado a cada semana, quais são os critérios de avaliação e como apresentar o trabalho de forma eficaz. Leia com atenção, pois você vai consultar este documento diversas vezes ao longo do semestre.
O Que é o Projeto Integrador e Por Que Ele Existe
Imagine que você leu um livro inteiro sobre natação. Você sabe a teoria dos movimentos, conhece os diferentes estilos e entende como a água se comporta. Mas você sabe nadar? Provavelmente não — pelo menos não ainda. Para aprender a nadar de verdade, você precisa entrar na piscina. O Projeto Integrador é a sua piscina.
A disciplina Sistemas Móveis adota um conjunto de metodologias ativas de aprendizagem que colocam você no centro do processo. A Sala de Aula Invertida faz com que você estude o material em casa antes das aulas, reservando o tempo em sala para discussões e prática. A Aprendizagem Baseada em Projeto garante que o conhecimento seja sempre aplicado em um contexto real e significativo. A Cultura Maker — o “aprender fazendo” — permeia cada atividade. O Design Thinking orienta como você vai pensar nos problemas do usuário. E a Aprendizagem Baseada em Times faz com que você construa não apenas competências técnicas, mas também a capacidade de trabalhar e se comunicar dentro de uma equipe de desenvolvimento.
O Projeto Integrador é onde todas essas metodologias se encontram. Ele existe porque aprender a programar lendo um tutorial é completamente diferente de aprender a programar resolvendo um problema real, com prazos, com colegas de time, com decisões arquiteturais para tomar e com um usuário final em mente. Essa diferença define a qualidade do profissional que você se tornará.
A Ideia Central: Um Aplicativo Construído Módulo a Módulo
O Projeto Integrador não é uma tarefa que você recebe no início e entrega no final. Ele cresce junto com a disciplina. A cada semana, depois de estudar um novo módulo, o seu grupo incorpora aquele conhecimento ao projeto. No Módulo 01, você configura o ambiente e cria a estrutura inicial. No Módulo 02, começa a escrever as primeiras classes em Dart. No Módulo 03, constrói os primeiros widgets. E assim sucessivamente, módulo por módulo, semana a semana, até que no Módulo 15 você tenha uma aplicação completa, com backend integrado, autenticação, notificações push e testes automatizados.
Essa estrutura tem um propósito muito específico: ela garante que você nunca acumule dívida técnica sem perceber. Ao entregar algo funcional a cada semana, você mantém o projeto saudável, recebe feedback contínuo do professor e tem clareza sobre o que já foi feito e o que ainda falta.
%%| label: etapas-projeto
%%| fig-cap: Etapas do Projeto Integrador
%%| fig-width: 11
flowchart TD
M01["M01<br/>Ambiente"] --> M02["M02<br/>Modelos Dart"]
M02 --> M03["M03<br/>Widgets"]
M03 --> M04["M04<br/>Layouts"]
M04 --> M05["M05<br/>Navegação"]
M05 --> M06["M06<br/>Formulários"]
M06 --> M07["M07<br/>Estado"]
M07 --> M08["M08<br/>SQLite"]
M08 --> M09["M09<br/>API REST"]
M09 --> M10["M10<br/>AWS"]
M10 --> EP{{"Entrega<br/>Parcial"}}
EP --> M11["M11<br/>Auth"]
M11 --> M12["M12<br/>Push"]
M12 --> M13["M13<br/>Nativos"]
M13 --> M14["M14<br/>i18n"]
M14 --> M15["M15<br/>Testes"]
M15 --> EF{{"Entrega<br/>Final"}}
style EP fill:#fff3cd,stroke:#ffc107,stroke-width:2px
style EF fill:#d4edda,stroke:#28a745,stroke-width:2px
Formação das Equipes e Escolha do Projeto
Cada equipe será formada por quatro ou cinco estudantes. A composição ideal mistura pessoas com diferentes pontos fortes: alguém mais analítico para pensar na arquitetura, alguém mais criativo para pensar na interface, alguém mais organizado para cuidar da documentação e do diário, e assim por diante. Times homogêneos, onde todos têm exatamente o mesmo perfil, tendem a ter pontos cegos. Times diversificados são mais resilientes.
A escolha do projeto é livre — dentro de alguns parâmetros fundamentais. O aplicativo que o grupo vai desenvolver precisa ser suficientemente rico para cobrir todos os conteúdos dos 15 módulos. Isso significa que ele precisa ter, obrigatoriamente, uma interface com múltiplas telas, navegação entre elas, formulários com validação, gerenciamento de estado, persistência local com SQLite, comunicação com uma API REST no backend da AWS, autenticação de usuários com OAuth2, notificações push, acesso a pelo menos um recurso nativo do dispositivo (câmera ou localização), suporte a múltiplos idiomas e testes automatizados.
Além disso, o projeto precisa ter um propósito real: um aplicativo que resolva um problema concreto para algum grupo de pessoas. Exemplos de domínios adequados incluem gestão de tarefas e produtividade, controle de saúde e bem-estar, registro e monitoramento ambiental, organização de eventos e grupos, aprendizagem e quizzes, catálogos de produtos ou serviços, entre outros. O importante é que o grupo consiga descrever claramente quem é o usuário do aplicativo e qual problema o aplicativo resolve para ele.
Antes de começar a desenvolver qualquer coisa, o grupo deve apresentar ao professor uma proposta de uma página descrevendo o aplicativo, o problema que ele resolve, o público-alvo e uma lista inicial das principais funcionalidades planejadas. Essa proposta deve ser aprovada pelo professor antes do grupo avançar para as atividades do Módulo 02.
O Diário de Desenvolvimento
Uma das práticas mais importantes que você vai adotar neste projeto é a manutenção de um diário de desenvolvimento. A cada aula prática, após concluir as atividades do dia, o grupo deve registrar neste diário tudo o que foi feito, as dificuldades encontradas, como elas foram superadas e o que o grupo pretende fazer na próxima semana.
Esse hábito não é burocrático nem é um exercício de escrita criativa. É uma ferramenta de aprendizagem. Quando você para para escrever o que fez, é forçado a processar e organizar o que aprendeu. Quando você descreve uma dificuldade e como a superou, está construindo uma base de conhecimento que vai ajudá-lo — e ao seu time — no futuro. E quando você planeja o que vem a seguir, está praticando a habilidade de gestão de projeto que todo desenvolvedor profissional precisa ter.
O diário será mantido em um arquivo dentro do repositório do projeto, e o professor terá acesso a ele a qualquer momento. Um template para o diário será fornecido pelo professor separadamente. O grupo é responsável por preencher o diário após cada aula prática, sem exceção.
%%| label: aulas-praticas
%%| fig-cap: Aulas práticas
%%| fig-width: 11
flowchart TD
A["Aulas Práticas<br/>(4)"] --> B["Desenvolvimento<br/>das Atividades"]
B --> C["Registro no<br/>Diário de Desenvolvimento"]
C --> D["O que foi feito?"]
C --> E["Quais dificuldades<br/>foram encontradas?"]
C --> F["Como as dificuldades<br/>foram superadas?"]
C --> G["O que esperar<br/>da próxima semana?"]
D & E & F & G --> H["Commit no<br/>Repositório GitHub"]
style A fill:#cfe2ff,stroke:#0d6efd
style H fill:#d4edda,stroke:#28a745
Estrutura das Aulas Práticas
A cada semana, as quatro aulas práticas totalizando 200 minutos são o coração do Projeto Integrador. Esse tempo é dedicado inteiramente ao desenvolvimento: o professor está disponível para tirar dúvidas, orientar decisões arquiteturais, apontar problemas antes que eles se tornem grandes e desafiar o grupo a pensar com mais profundidade.
A dinâmica esperada nessas aulas segue um padrão que o grupo deve internalizar rapidamente. Nos primeiros minutos, o grupo revisa rapidamente o que foi feito na semana anterior, discute o planejamento do dia com base nas atividades do módulo atual e divide as tarefas entre os membros. Em seguida, a maior parte do tempo é dedicada ao desenvolvimento propriamente dito. Nos minutos finais, o grupo consolida o que foi desenvolvido, resolve conflitos no Git, faz o commit das alterações e atualiza o diário.
Essa estrutura não é rígida: ela é uma orientação. O grupo vai desenvolver seu próprio ritmo ao longo do semestre. O que não é opcional é o comprometimento de cada membro com as atividades do grupo e a entrega consistente a cada semana.
Atividades por Módulo
Módulo 01 — Ambiente, Git e Estrutura Inicial
O primeiro encontro com o Projeto Integrador é sobre preparação e alinhamento. Antes de escrever uma linha de código, o grupo precisa ter uma base sólida. A primeira aula prática é dedicada à proposta do projeto: o grupo discute ideias, converge em um tema, define o problema a ser resolvido, identifica o público-alvo e esboça as funcionalidades principais. Essa discussão deve culminar na escrita colaborativa da proposta de uma página mencionada anteriormente.
Na segunda aula prática, cada membro configura o ambiente de desenvolvimento em sua própria máquina: Flutter SDK, Android Studio com emulador configurado, Visual Studio Code com as extensões necessárias. Um membro é designado como responsável pelo repositório e cria o projeto no GitHub. Os demais fazem o fork ou recebem acesso de colaborador. Um projeto Flutter inicial é criado e o primeiro commit é feito.
Na terceira aula prática, o grupo verifica que todos os membros conseguem clonar o repositório, executar o projeto no emulador e fazer commits e pushes sem problemas. O grupo também esboça, em papel ou em uma ferramenta de prototipação simples, as telas principais do aplicativo e o fluxo de navegação entre elas. Esse esboço é fotografado e adicionado ao repositório como documentação inicial.
Ao final do Módulo 01, o grupo entrega ao professor: a proposta aprovada do projeto, o repositório configurado e acessível, o projeto Flutter rodando no emulador e o esboço inicial das telas no repositório.
Módulo 02 — Primeiros Modelos de Domínio em Dart
Com o ambiente configurado e a proposta aprovada, o Módulo 02 é o momento de transformar as ideias em código Dart. O foco está nos modelos de domínio: as classes que representam os conceitos principais do seu aplicativo. Se o seu projeto é um aplicativo de tarefas, os modelos podem incluir Tarefa, Categoria e Usuario. Se é um aplicativo de saúde, podem ser Registro, Medicamento e Perfil.
Na primeira aula prática, o grupo identifica os principais conceitos do domínio do aplicativo e esboça um diagrama de classes. Cada classe deve ter atributos bem definidos, tipos adequados e documentação em formato de comentário Dart. Na segunda aula prática, as classes são implementadas seguindo os conceitos estudados no Módulo 02: construtores nomeados, null safety, métodos copyWith para imutabilidade, e sobrescrita de toString, == e hashCode. Na terceira aula prática, o grupo escreve testes unitários básicos para cada classe de modelo, verificando que os construtores, a igualdade e as conversões funcionam corretamente.
O entregável ao final do Módulo 02 é uma pasta lib/models/ com os modelos de domínio implementados, documentados e testados, com o commit correspondente no repositório e o diário atualizado.
Módulo 03 — Primeiros Widgets e Tela Inicial
No Módulo 03, o projeto ganha forma visual pela primeira vez. Com os modelos de domínio prontos, o grupo começa a construir a interface. O objetivo deste módulo é implementar a tela inicial do aplicativo com widgets reais, utilizando os conceitos estudados: Scaffold, AppBar, Text, Image, Icon, StatefulWidget e o ciclo de vida de widgets.
Na primeira aula prática, o grupo define a paleta de cores, a tipografia e o estilo visual geral do aplicativo, criando um arquivo lib/theme/app_theme.dart com as definições de tema. Na segunda aula prática, a tela inicial é construída com widgets reais — não placeholders — exibindo dados dos modelos de domínio criados no módulo anterior. Na terceira aula prática, o grupo identifica quais partes da tela têm estado local (como um campo de busca ou um contador) e refatora esses componentes para StatefulWidget com uso adequado de setState.
O entregável é a tela inicial funcional rodando no emulador, com o tema definido e os primeiros widgets reais implementados.
Módulo 04 — Layouts das Telas Principais
Com os primeiros widgets funcionando, o Módulo 04 expande o aplicativo para suas telas principais. O foco é na organização visual: usar Row, Column, ListView, GridView, Stack e as ferramentas de responsividade para criar interfaces que se adaptem a diferentes tamanhos de tela.
Na primeira aula prática, o grupo identifica as três ou quatro telas mais importantes do aplicativo e decompõe cada uma em widgets menores e reutilizáveis. Essa decomposição é documentada como um diagrama de árvore de widgets. Na segunda aula prática, os layouts dessas telas são implementados, com especial atenção ao uso correto de Expanded, Flexible e Spacer dentro de linhas e colunas. Na terceira aula prática, o grupo verifica o comportamento dos layouts em diferentes tamanhos de tela usando o emulador com resoluções diferentes, corrigindo problemas de overflow e adaptando layouts com MediaQuery onde necessário.
O entregável é um conjunto de telas com layouts responsivos implementados, com o diagrama de árvore de widgets incluído na documentação do repositório.
Módulo 05 — Navegação entre Telas com go_router
O Módulo 05 conecta as telas do aplicativo. Com go_router, o grupo implementa a navegação completa do aplicativo: rotas nomeadas, passagem de parâmetros entre telas, navegação profunda e a estrutura de navegação persistente (barra inferior ou drawer).
Na primeira aula prática, o grupo cria o arquivo lib/router/app_router.dart com a configuração completa do go_router, definindo todas as rotas planejadas para o aplicativo, incluindo subrotas quando aplicável. Na segunda aula prática, os elementos de navegação da interface — botões, ícones, itens de menu — são conectados às rotas definidas, e o grupo verifica que a navegação entre telas funciona corretamente. Na terceira aula prática, o grupo implementa um guard de navegação básico: se o usuário não está autenticado (usando uma variável booleana simples por enquanto, já que a autenticação real vem no Módulo 11), ele é redirecionado para uma tela de boas-vindas ou de login simulado.
O entregável é o fluxo de navegação completo do aplicativo funcionando, com todas as telas conectadas e o guard de autenticação básico implementado.
Módulo 06 — Formulários com Validação
O Módulo 06 adiciona ao aplicativo a capacidade de coletar informações do usuário de forma robusta. Todos os formulários do aplicativo — cadastro, login, criação de registros, busca — são implementados com validação adequada e feedback visual claro.
Na primeira aula prática, o grupo lista todos os formulários necessários no aplicativo e define os campos e as regras de validação de cada um. Na segunda aula prática, os formulários são implementados com Form, TextFormField, validadores customizados e o gerenciamento de foco com FocusNode. Na terceira aula prática, o grupo adiciona o loading_overlay para feedback de carregamento durante o envio de formulários, e implementa SnackBar e Dialog para comunicar resultados ao usuário. Os formulários são testados com entradas válidas e inválidas para garantir que a validação funciona corretamente em todos os casos.
O entregável são os formulários completos com validação, feedback de carregamento e mensagens de resultado implementados.
Módulo 07 — Gerenciamento de Estado com Provider
O Módulo 07 é o módulo mais importante da primeira metade da disciplina, e também o mais desafiador. Aqui, o grupo refatora toda a arquitetura do aplicativo para usar Provider e GetIt, separando claramente a lógica de negócios da interface de usuário.
Na primeira aula prática, o grupo identifica todo o estado da aplicação que precisa ser compartilhado entre telas — dados do usuário logado, listas de itens, filtros ativos, preferências — e projeta as classes ChangeNotifier que vão gerenciar esse estado. Na segunda aula prática, essas classes são implementadas e registradas no GetIt e no MultiProvider na raiz da aplicação. Os widgets que dependem desse estado são atualizados para usar Consumer, Selector ou context.watch. Na terceira aula prática, o grupo verifica que a refatoração não quebrou nenhuma funcionalidade existente e faz os ajustes necessários.
%%| label: entrega-parcial
%%| fig-cap: Entrega parcial
%%| fig-width: 11
flowchart TB
subgraph Apresentacao["Entrega Parcial<br/>Módulo 10"]
A["Demonstração do App<br/>(5 minutos)"] --> B["Explicação das Decisões<br/>Arquiteturais (3 minutos)"]
B --> C["Perguntas do<br/>Professor (2 minutos)"]
end
subgraph Avaliacao["Critérios de Avaliação — Entrega Parcial"]
D["Funcionalidades<br/>Implementadas"]
E["Qualidade do<br/>Código e Arquitetura"]
F["Diário de<br/>Desenvolvimento"]
G["Apresentação<br/>e Comunicação"]
end
Apresentacao --> Avaliacao
style Apresentacao fill:#fff3cd,stroke:#ffc107
style Avaliacao fill:#f8f9fa,stroke:#6c757d
Módulo 08 — Persistência Local com SQLite
Com a arquitetura do Provider estabelecida, o Módulo 08 adiciona ao aplicativo a capacidade de guardar dados localmente, para que o usuário não perca suas informações quando fecha o aplicativo ou fica sem conexão.
Na primeira aula prática, o grupo projeta o schema do banco de dados SQLite do aplicativo: quais tabelas são necessárias, quais são os campos de cada tabela e quais são as relações entre elas. Esse schema é documentado como um diagrama entidade-relacionamento e commitado no repositório. Na segunda aula prática, o banco de dados é criado usando sqflite, e as operações CRUD para cada entidade são implementadas em classes de repositório localizadas em lib/data/repositories/. Na terceira aula prática, os repositórios locais são integrados com os ChangeNotifier do Provider: agora quando o estado é atualizado, os dados são automaticamente persistidos no banco local.
O entregável é o banco de dados SQLite funcionando com todas as operações CRUD implementadas e integradas ao Provider, com o diagrama do schema no repositório.
Módulo 09 — Consumo de API REST
O Módulo 09 conecta o aplicativo ao mundo externo. Neste módulo, o grupo implementa a camada de comunicação com a API do backend, que será completamente construída no Módulo 10. Por ora, o grupo pode usar uma API pública existente ou um servidor local simples para testar a integração.
Na primeira aula prática, o grupo define o contrato da API do projeto: quais endpoints existirão, quais dados cada um recebe e retorna, e quais são os códigos de resposta esperados. Esse contrato é documentado em um arquivo doc/api-contract.md no repositório. Na segunda aula prática, a camada de serviços é implementada em lib/data/services/, com classes que usam o pacote http para fazer requisições e retornar modelos Dart desserializados do JSON. Na terceira aula prática, a lógica de cache é implementada: antes de buscar dados da API, o app verifica se há dados locais recentes; se houver, usa os locais; se não, busca da API e salva localmente.
O entregável é a camada de serviços HTTP completa, com serialização JSON, tratamento de erros e lógica de cache, integrada aos repositórios e ao Provider.
Módulo 10 — Backend Serverless na AWS (Marco da Entrega Parcial)
O Módulo 10 é o momento em que o grupo constrói o backend real do aplicativo na Amazon Web Services. Com API Gateway, Lambda Functions, RDS PostgreSQL e SQS, o grupo implementa o servidor que alimentará o aplicativo em produção.
Na primeira aula prática, um membro do grupo cria a conta AWS gratuita (ou usa uma já existente) e configura o RDS PostgreSQL com o schema definido no Módulo 08. As tabelas do banco de dados remoto devem espelhar a estrutura do banco local SQLite. Na segunda aula prática, as Lambda Functions são escritas — em Python ou Node.js, a critério do grupo — para cada endpoint definido no contrato da API do Módulo 09. As funções são testadas individualmente no console AWS antes de serem expostas pelo API Gateway. Na terceira aula prática, o API Gateway é configurado com os endpoints correspondentes às funções Lambda, e a aplicação Flutter é atualizada para apontar para a URL real do backend na AWS em vez de um servidor local ou API de teste.
O entregável é o backend funcionando na AWS, com todos os endpoints do contrato implementados e a aplicação Flutter integrada ao backend real.
Este módulo coincide com a Entrega Parcial. Ao final, o grupo realiza uma apresentação de 10 minutos para o professor, demonstrando o aplicativo funcionando e explicando as decisões arquiteturais tomadas. A entrega inclui o repositório atualizado, o diário de desenvolvimento dos módulos 01 a 10 e a apresentação realizada.
Módulo 11 — Autenticação Real com OAuth2
O guard de autenticação simples implementado no Módulo 05 agora ganha implementação real. No Módulo 11, o grupo implementa o fluxo completo de autenticação OAuth2 no aplicativo, com tokens armazenados de forma segura e renovação automática de sessão.
Na primeira aula prática, o grupo escolhe um provedor de identidade — pode ser o próprio backend AWS com Cognito, ou um provedor externo como Google ou GitHub — e configura o fluxo OAuth2 Authorization Code com PKCE usando o pacote oauth2_client. Na segunda aula prática, o flutter_secure_storage é integrado para armazenamento seguro dos tokens de acesso e de atualização, e um interceptor HTTP é implementado para adicionar automaticamente o token de autorização a cada requisição da API. Na terceira aula prática, o guard de navegação do go_router é atualizado para verificar o token real e redirecionar corretamente, e o fluxo de renovação automática de token é testado simulando a expiração.
O entregável é o fluxo de autenticação completo e funcional, com login, logout, armazenamento seguro de tokens e renovação automática de sessão.
Módulo 12 — Notificações Push com Firebase
Com a autenticação funcionando, o Módulo 12 adiciona ao aplicativo a capacidade de receber notificações push. Essa funcionalidade conecta o Firebase Cloud Messaging com o backend AWS para criar um ciclo completo de comunicação proativa com o usuário.
Na primeira aula prática, o grupo cria um projeto no console Firebase, integra os pacotes firebase_core e firebase_messaging ao projeto Flutter e configura a solicitação de permissão de notificação. Na segunda aula prática, os handlers de notificação são implementados para os três estados da aplicação (foreground, background e terminated), e a lógica de navegação ao tocar em uma notificação é configurada. Na terceira aula prática, o grupo atualiza uma das Lambda Functions do backend para enviar notificações usando o Firebase Admin SDK, testando o fluxo completo de ponta a ponta: uma ação do usuário no app dispara uma Lambda, que processa e envia uma notificação, que aparece no dispositivo.
O entregável é o sistema de notificações push funcionando de ponta a ponta, com envio pelo backend e recebimento pelo aplicativo nos três estados.
Módulo 13 — Recursos Nativos e Permissões
O Módulo 13 enriquece o aplicativo com recursos que só existem em dispositivos móveis: câmera, localização GPS ou sensores. O grupo deve implementar pelo menos um desses recursos de forma significativa para o domínio do projeto.
Na primeira aula prática, o grupo decide qual recurso nativo faz mais sentido para o aplicativo e qual é a experiência do usuário desejada. Exemplos: um aplicativo de tarefas pode permitir que o usuário fotografe um documento como anexo; um aplicativo de saúde pode registrar a localização do usuário durante uma corrida; um aplicativo de evento pode detectar o dispositivo sendo agitado como ação rápida. Na segunda aula prática, as permissões necessárias são configuradas nos manifestos do Android e a solicitação de permissão com permission_handler é implementada com uma experiência de usuário adequada — explicação antes de pedir, tratamento elegante da recusa. Na terceira aula prática, o recurso nativo é integrado ao fluxo principal do aplicativo, os dados capturados são armazenados localmente e sincronizados com o backend quando aplicável.
O entregável é pelo menos um recurso nativo integrado de forma funcional ao fluxo principal do aplicativo.
Módulo 14 — Internacionalização
O Módulo 14 prepara o aplicativo para ser usado por pessoas que falam idiomas diferentes. Com i18n_extension, o grupo traduz o aplicativo para pelo menos dois idiomas, implementa a detecção automática do idioma do sistema e oferece ao usuário a possibilidade de alterar o idioma manualmente.
Na primeira aula prática, o grupo identifica todas as strings visíveis ao usuário no aplicativo — títulos, rótulos, mensagens de erro, textos de botões, placeholders de campos — e cria um inventário completo. Na segunda aula prática, o i18n_extension é configurado, as strings são anotadas no código com .i18n e os arquivos de tradução para pelo menos dois idiomas são criados. Na terceira aula prática, o seletor de idioma é adicionado às configurações do aplicativo, e os formatos de data, hora e números são adaptados para cada locale usando a biblioteca intl.
O entregável é o aplicativo traduzido para dois ou mais idiomas, com seletor de idioma funcional e formatos regionais adaptados.
Módulo 15 — Testes, Qualidade e Publicação
O módulo final é sobre garantir que o que foi construído funciona de forma confiável e está pronto para ser entregue ao mundo. O grupo escreve testes automatizados, analisa e corrige problemas de qualidade de código e prepara o build de release do aplicativo.
Na primeira aula prática, testes unitários são escritos para as classes de modelo e os repositórios, e testes de widget são escritos para os formulários principais. Na segunda aula prática, um teste de integração é escrito para o fluxo mais importante do aplicativo — o fluxo que o usuário realiza com mais frequência. O flutter_lints é executado e todos os avisos críticos são resolvidos. Na terceira aula prática, o grupo gera o build de release para Android, configura os ícones e a splash screen e prepara o pacote para submissão à Google Play Store (o processo de submissão real pode ser simulado, dependendo das contas disponíveis).
Esta é a última aula prática antes da Entrega Final. O grupo deve garantir que o repositório está limpo, documentado e que o diário está completamente atualizado antes de encerrar a aula.
O entregável final inclui testes automatizados rodando sem falhas, análise de lints sem erros críticos e o build de release gerado.
Cronograma Geral
%%| label: linha-tempo-projeto
%%| fig-cap: Linha do Tempo do Projeto Integrador
%%| fig-width: 11
gantt
title Linha do Tempo do Projeto Integrador
dateFormat DD/MM
axisFormat %d/%m
tickInterval 1week
weekday monday
todayMarker stroke-width:2px,stroke:#0f0,opacity:0.5
excludes 15/02, 16/02, 17/02, 18/02, 19/02, 20/02, 21/02, 03/04, 20/04, 21/04, 01/05, 04/06, 05/06
section Fundamentos Flutter
Inicio :milestone, inicio, 02/02, 1w
Configuração e repositório :m1, after inicio, 1w
Modelos de domínio em Dart :m2, after m1, 1w
Primeiros widgets da interface :m3, after m2, 1w
section Interface do Usuário
Layouts das telas principais :m4, after m3, 1w
Navegação entre telas :m5, after m4, 1w
Formulários com validação :m6, after m5, 1w
section Arquitetura e Dados
Estado global com Provider :m7, after m6, 1w
section Integração e Serviços
Persistência local com SQLite :m8, after m7, 1w
Consumo da API do backend :m9, after m8, 10d
Integração com AWS :m10, after m9, 11d
Entrega Parcial :milestone, after m10, 0d
section Recursos Avançados
Autenticação OAuth2 :m11, after m10, 1w
Notificações push :m12, after m11, 1w
Câmera e localização :m13, after m12, 1w
Internacionalização :m14, after m13, 1w
section Qualidade e Entrega
Testes e build de release :m15, after m14, 1w
Entrega Final :milestone, after m15, 0d
Instruções para Entregas
Entrega Parcial — ao final do Módulo 10
A Entrega Parcial ocorre ao final do décimo módulo e representa 20% da nota total da disciplina. Para realizá-la, o grupo deve garantir que o repositório no GitHub está atualizado com todos os commits das semanas anteriores, que o diário de desenvolvimento cobre todos os módulos de 01 a 10, e que o aplicativo está funcionando no emulador — mesmo que com dados simulados ou hardcoded onde a integração com o backend ainda não foi feita.
A apresentação da Entrega Parcial tem duração de 10 minutos e segue esta estrutura: dois minutos de apresentação do problema e do aplicativo, cinco minutos de demonstração do aplicativo rodando e três minutos de explicação das decisões arquiteturais tomadas (por que o grupo estruturou o Provider desta forma, quais widgets foram criados como reutilizáveis, como está organizada a navegação). O professor fará perguntas durante e após a demonstração.
Cada membro do grupo deve participar da apresentação. O grupo que apresentar um aplicativo onde apenas um membro sabe explicar o que foi feito será avaliado de forma diferente de um grupo onde todos dominam o que foi construído.
Entrega Final — semana seguinte ao Módulo 15
A Entrega Final ocorre na semana seguinte ao término do Módulo 15 e representa 40% da nota total da disciplina. Para realizá-la, o grupo deve garantir que o repositório está completamente organizado, que todos os testes automatizados estão passando, que o diário cobre todos os 15 módulos, que a documentação está atualizada e que o build de release foi gerado.
A apresentação final tem duração de 20 minutos e segue esta estrutura: três minutos de contextualização do problema e do público-alvo, oito minutos de demonstração do aplicativo com foco nos recursos mais complexos (autenticação, notificações, recursos nativos, internacionalização, testes), cinco minutos de apresentação da arquitetura do projeto com foco na separação de camadas e nas decisões técnicas tomadas, e quatro minutos de lições aprendidas — o que o grupo faria diferente se começasse do zero.
A Entrega Final é avaliada tanto pela qualidade técnica da implementação quanto pela qualidade da apresentação e pelo domínio que cada membro demonstra sobre o que foi construído.
Critérios de Avaliação
A avaliação do Projeto Integrador considera múltiplas dimensões, porque desenvolvimento de software profissional envolve muito mais do que escrever código que funciona. Um profissional completo entrega código de qualidade, documenta seu trabalho, comunica suas decisões com clareza e colabora de forma efetiva com sua equipe.
A implementação técnica é avaliada pela completude das funcionalidades planejadas, pela ausência de erros críticos, pela cobertura de casos de erro e estados de loading, e pela correta utilização das tecnologias estudadas em cada módulo. Um aplicativo que funciona perfeitamente em condições ideais mas trava quando a internet está lenta ou quando o usuário insere dados inesperados não está completo.
A qualidade do código é avaliada pela organização das pastas e arquivos, pela consistência das convenções de nomenclatura, pela separação adequada de responsabilidades entre camadas (modelos, repositórios, providers, widgets), pela presença de comentários onde a lógica não é imediatamente óbvia e pelo cumprimento das análises do flutter_lints.
A documentação é avaliada pela completude e honestidade do diário de desenvolvimento, pela clareza do README do repositório, pela presença do diagrama de telas e fluxo de navegação e pelo contrato da API documentado.
A apresentação é avaliada pela clareza e objetividade da comunicação, pela capacidade de demonstrar o aplicativo de forma fluida sem surpresas negativas, pela profundidade das explicações técnicas e pela participação de todos os membros do grupo.
%%| label: nota_projeto
%%| fig-cap: Composição da Nota do Projeto Integrador
%%| fig-width: 11
pie title Composição da Nota<br/>Projeto Integrador (60% da nota total)
"Implementação Técnica" : 40
"Qualidade do Código" : 25
"Documentação e Diário" : 20
"Apresentação" : 15
Dicas para uma Apresentação Técnica Eficaz
Uma apresentação técnica bem feita não é sobre impressionar com jargões ou mostrar a quantidade de linhas de código escritas. É sobre comunicar com clareza o problema que foi resolvido, as decisões que foram tomadas e por que elas fazem sentido. Estas são as práticas que fazem a diferença entre uma apresentação boa e uma excelente.
Prepare o ambiente com antecedência. Teste o aplicativo no emulador antes de começar. Certifique-se de que o emulador está responsivo, que a conexão com o backend está funcionando e que os dados de teste estão carregados. Uma demonstração que começa com “espera um segundo, está demorando para abrir” deixa uma impressão ruim, mesmo que o que vier a seguir seja excelente.
Conte uma história. Não demonstre funcionalidades de forma aleatória. Crie um cenário: “Imagine que você é uma pessoa que usa nosso aplicativo pela primeira vez…” e mostre o fluxo completo, do login até a realização da ação principal. Isso é muito mais envolvente do que mostrar uma tela por vez de forma desconectada.
Explique as decisões, não apenas as implementações. Em vez de dizer “aqui usamos Provider”, diga “aqui precisávamos compartilhar o estado da lista de itens entre três telas diferentes, então optamos por Provider porque…”. A explicação do porquê é o que demonstra compreensão profunda.
Antecipe as perguntas difíceis. Se vocês tomaram uma decisão técnica que pode parecer questionável — como usar dados hardcoded em algum lugar, ou simplificar uma funcionalidade — mencione isso proativamente e explique o raciocínio. Isso mostra maturidade e honestidade.
Distribua a apresentação entre todos os membros. Divida as partes da apresentação de forma que cada membro apresente uma seção. Isso demonstra que o conhecimento é distribuído pelo time, não concentrado em uma única pessoa.
Trate o professor como um cliente técnico. Ele conhece as tecnologias tão bem quanto vocês ou melhor. Não simplifique demais, mas também não entre em detalhes irrelevantes. O nível certo de profundidade é: suficiente para demonstrar que vocês entendem o que construíram e as implicações das decisões tomadas.
Organização do Repositório
O repositório GitHub do projeto deve seguir a estrutura definida abaixo. A ideia geral é que o repositório seja a documentação viva do projeto: alguém que clona o repositório e lê o README deve conseguir entender o que o aplicativo faz, como o projeto está organizado, como executar o aplicativo localmente e como contribuir.
Seguir essa estrutura não é uma formalidade burocrática: ela reflete decisões arquiteturais que você vai estudar ao longo do semestre e que são amplamente adotadas por equipes profissionais de desenvolvimento de software. Quando o professor ou um colega de equipe abrir o repositório pela primeira vez, a estrutura de pastas já deve contar uma história sobre o que o projeto faz e como ele foi organizado.
A estrutura é baseada em dois princípios que se complementam: a Arquitetura Hexagonal, que protege o núcleo de negócios da aplicação de mudanças tecnológicas, e o Domain-Driven Design (DDD), que organiza o código em torno dos conceitos do domínio do problema, e não em torno de tecnologias ou camadas genéricas. Você não precisa dominar esses conceitos antes de começar — eles vão fazer cada vez mais sentido à medida que você avançar nos módulos. Por ora, siga a estrutura, e o entendimento virá com a prática.
Além do código-fonte do aplicativo Flutter e do backend AWS (scripts das Lambda Functions e configurações do API Gateway), o repositório deve conter uma pasta doc/ com o diagrama de telas e fluxo de navegação, o contrato da API, o schema do banco de dados e o diário de desenvolvimento. Cada entregável semanal deve ter um commit correspondente com uma mensagem de commit descritiva — não apenas “commit” ou “atualização”, mas algo como “M05: implementa navegação com go_router e guard de autenticação”.
O uso de branches é encorajado mas não obrigatório. Se o grupo optar por usar branches, a convenção sugerida é uma branch por módulo que é mergeada na branch principal ao final de cada semana. Se o grupo optar por trabalhar diretamente na branch principal, é importante que os commits sejam frequentes e bem descritos.
Para facilitar a sua vida, baixe um dos scripts para criar a estrutura de pastas completa: Windows ou linux.
Visão Geral: As Quatro Raízes
A raiz do repositório contém exatamente quatro pastas principais. Essa restrição é intencional: quanto menos pastas na raiz, mais clara é a divisão de responsabilidades do projeto.
A pasta frontend contém o projeto Flutter completo, que é o aplicativo móvel que o usuário final vai instalar no celular. A pasta backend contém toda a infraestrutura serverless da AWS: as Lambda Functions, os scripts de criação do banco de dados e as configurações de infraestrutura. A pasta integracao contém os contratos de API, os scripts de teste de integração de ponta a ponta e os mocks compartilhados entre frontend e backend. A pasta doc contém toda a documentação do projeto, incluindo o diário de desenvolvimento, os diagramas de arquitetura e os registros de decisões técnicas.
%%| label: pastas-raizes
%%| fig-cap: Pastas raízes do Projeto
%%| fig-width: 11
graph TD
ROOT["meu-projeto/"] --> FE["frontend/<br/>Aplicativo Flutter"]
ROOT --> BE["backend/<br/>AWS Lambda + RDS + SQS"]
ROOT --> INT["integracao/<br/>Contratos e Testes E2E"]
ROOT --> DOC["doc/<br/>Documentação e Diário"]
style ROOT fill:#e8eaf6,stroke:#3949ab
style FE fill:#e3f2fd,stroke:#1976d2
style BE fill:#fff3e0,stroke:#f57c00
style INT fill:#f3e5f5,stroke:#7b1fa2
style DOC fill:#e8f5e9,stroke:#388e3c
A Pasta frontend — O Aplicativo Flutter
O projeto Flutter dentro de frontend adota a abordagem Feature-First com Arquitetura Hexagonal. Isso significa que, ao invés de organizar o código por tipo de arquivo (uma pasta para todos os widgets, outra para todos os modelos, outra para todos os serviços), o código é organizado por funcionalidade de negócio. Cada funcionalidade encapsula suas próprias camadas de apresentação, domínio e dados. Essa abordagem torna o projeto muito mais fácil de navegar: quando você precisa trabalhar na funcionalidade de autenticação, tudo o que você precisa está em um único lugar.
%%| label: pasta-frontend
%%| fig-cap: Pasta frontend
%%| fig-width: 11
graph TD
FE["frontend/"] --> PUBSPEC["pubspec.yaml"]
FE --> LIB["lib/"]
FE --> TEST["test/"]
FE --> ANDROID["android/"]
FE --> IOS["ios/"]
FE --> ASSETS["assets/"]
FE --> INTEGRATION["integration_test/"]
LIB --> MAIN["main.dart"]
LIB --> APP["app/"]
LIB --> CORE["core/"]
LIB --> FEATURES["features/"]
APP --> APP_WIDGET["app_widget.dart"]
APP --> APP_ROUTER["app_router.dart"]
APP --> APP_THEME["app_theme.dart"]
APP --> APP_PROVIDERS["app_providers.dart"]
CORE --> COREDI["di/<br/>Injeção de dependências"]
CORE --> COREERROR["error/<br/>Falhas e exceções do domínio"]
CORE --> CORENET["network/<br/>Cliente HTTP e interceptors"]
CORE --> COREUTILS["utils/<br/>Utilitários globais"]
CORE --> COREWIDGETS["widgets/<br/>Widgets reutilizáveis"]
CORE --> CORETHEME["theme/<br/>Tokens de design"]
FEATURES --> FEAT_AUTH["auth/"]
FEATURES --> FEAT_HOME["home/"]
FEATURES --> FEAT_N["... outras features"]
style FE fill:#e3f2fd,stroke:#1976d2
style LIB fill:#bbdefb,stroke:#1976d2
style FEATURES fill:#c8e6c9,stroke:#388e3c
style CORE fill:#fff9c4,stroke:#f9a825
A Camada core — Fundações Compartilhadas
A pasta core contém tudo aquilo que é compartilhado entre as funcionalidades do projeto, mas que não pertence a nenhuma funcionalidade específica. Pense nela como a biblioteca interna do seu projeto: coisas que qualquer funcionalidade pode usar, mas que não têm um “dono” específico.
A subpasta di (Dependency Injection) é especialmente importante. É aqui que o GetIt é configurado, registrando todas as implementações concretas que serão injetadas quando o código solicitar uma interface. Quando o módulo de autenticação precisar de um repositório de usuários, ele vai pedir ao GetIt uma instância de IAuthRepository, e o GetIt vai fornecer a implementação concreta que foi registrada aqui. Isso é o que permite que você substitua implementações reais por mocks nos testes, sem alterar uma linha de código de negócio.
frontend/
└── lib/
└── core/
├── di/
│ ├── injection_container.dart # Ponto central de configuração do GetIt
│ └── injection_container.config.dart # Arquivo gerado (se usar injectable)
├── error/
│ ├── failures.dart # Classes de falha do domínio (NetworkFailure, etc.)
│ └── exceptions.dart # Exceções de infraestrutura (ServerException, etc.)
├── network/
│ ├── http_client.dart # Configuração do cliente http com interceptors
│ ├── auth_interceptor.dart # Adiciona token de autorização automaticamente
│ └── network_info.dart # Verifica disponibilidade de conexão
├── storage/
│ ├── secure_storage_service.dart # Abstração do flutter_secure_storage
│ └── preferences_service.dart # Abstração do SharedPreferences
├── utils/
│ ├── date_formatter.dart # Formatação de datas por locale
│ ├── validators.dart # Validadores reutilizáveis (CPF, email, etc.)
│ └── extensions/
│ ├── string_extensions.dart # Extensões para String
│ └── context_extensions.dart # Extensões para BuildContext
├── widgets/
│ ├── app_button.dart # Botão padrão da aplicação
│ ├── app_text_field.dart # Campo de texto padrão
│ ├── loading_overlay_widget.dart # Indicador de carregamento global
│ ├── error_widget.dart # Widget padrão de exibição de erro
│ └── empty_state_widget.dart # Widget para listas vazias
└── theme/
├── app_colors.dart # Paleta de cores da aplicação
├── app_typography.dart # Estilos de texto
└── app_spacing.dart # Espaçamentos e tamanhos
A Estrutura de uma Feature — O Hexágono em Ação
Esta é a parte mais importante da estrutura. Cada funcionalidade do aplicativo — autenticação, listagem de itens, perfil do usuário — segue o mesmo padrão interno. Quando você aprende a estrutura de uma feature, você sabe a estrutura de todas.
O diagrama abaixo mostra como as camadas se relacionam dentro de uma feature. O domínio no centro não conhece nada sobre Flutter, HTTP ou banco de dados. A infraestrutura implementa os contratos definidos pelo domínio. A apresentação usa os casos de uso do domínio através do Provider.
%%| label: estrutura-feature
%%| fig-cap: Estrutura de uma feature
%%| fig-width: 11
graph LR
subgraph PRES["Apresentação (Adaptadores Primários)"]
WIDGETS["Widgets<br/>(telas e componentes)"]
PROVIDERS["Providers<br/>(gerenciamento de estado)"]
end
subgraph DOMAIN["Domínio (Núcleo Protegido)"]
ENTITIES["Entidades e<br/>Objetos de Valor"]
USECASES["Casos de Uso<br/>(lógica de negócio)"]
REPO_IFACE["Contratos de<br/>Repositório (interfaces)"]
end
subgraph INFRA["Infraestrutura (Adaptadores Secundários)"]
REPO_IMPL["Implementações<br/>de Repositório"]
DATASOURCES["Data Sources<br/>(HTTP, SQLite)"]
DTOS["DTOs<br/>(modelos de serialização)"]
end
WIDGETS --> PROVIDERS
PROVIDERS --> USECASES
USECASES --> ENTITIES
USECASES --> REPO_IFACE
REPO_IFACE -.->|"implementado por"| REPO_IMPL
REPO_IMPL --> DATASOURCES
DATASOURCES --> DTOS
DTOS -.->|"mapeado para"| ENTITIES
style DOMAIN fill:#fff9c4,stroke:#f9a825
style PRES fill:#e3f2fd,stroke:#1976d2
style INFRA fill:#fce4ec,stroke:#c2185b
A estrutura de pastas de uma feature segue exatamente esse diagrama:
frontend/
└── lib/
└── features/
└── auth/ # Feature de autenticação
├── domain/
│ ├── entities/
│ │ └── user.dart # Entidade User (sem dependências externas)
│ ├── value_objects/
│ │ ├── email.dart # Objeto de valor Email (com validação interna)
│ │ └── password.dart # Objeto de valor Password
│ ├── repositories/
│ │ └── i_auth_repository.dart # Contrato abstrato do repositório
│ └── usecases/
│ ├── sign_in_usecase.dart # Caso de uso: realizar login
│ ├── sign_out_usecase.dart # Caso de uso: realizar logout
│ └── get_current_user_usecase.dart
├── infrastructure/
│ ├── repositories/
│ │ └── auth_repository.dart # Implementação concreta do contrato
│ ├── datasources/
│ │ ├── auth_remote_datasource.dart # Comunicação com a API OAuth2
│ │ └── auth_local_datasource.dart # Leitura/escrita de tokens seguros
│ └── dtos/
│ └── user_dto.dart # DTO com fromJson/toJson para a API
└── presentation/
├── providers/
│ └── auth_provider.dart # ChangeNotifier que usa os casos de uso
├── pages/
│ ├── login_page.dart # Tela de login
│ └── splash_page.dart # Tela de splash com verificação de sessão
└── widgets/
├── login_form.dart # Formulário de login reutilizável
└── oauth_button.dart # Botão de login OAuth2
Estrutura Completa do Frontend
Abaixo está a estrutura completa do frontend, considerando todas as features que o projeto deve ter ao longo dos 15 módulos. As features marcadas com o número do módulo indicam em qual semana elas serão criadas ou significativamente expandidas.
frontend/
├── pubspec.yaml # Dependências do projeto Flutter
├── pubspec.lock
├── analysis_options.yaml # Configuração do flutter_lints
├── .gitignore
│
├── android/ # Configurações nativas Android
│ ├── app/
│ │ ├── src/main/AndroidManifest.xml # Permissões, deep links, FCM
│ │ └── google-services.json # Configuração Firebase (não commitar!)
│ └── build.gradle
│
├── ios/ # Configurações nativas iOS
│ └── Runner/
│ ├── Info.plist # Permissões iOS
│ └── GoogleService-Info.plist # Configuração Firebase iOS (não commitar!)
│
├── assets/
│ ├── images/ # Imagens e ícones da aplicação
│ ├── fonts/ # Fontes customizadas (se houver)
│ └── i18n/ # Arquivos de tradução (Módulo 14)
│ ├── strings_pt.i18n.dart
│ └── strings_en.i18n.dart
│
├── integration_test/ # Testes de integração E2E (Módulo 15)
│ └── app_test.dart
│
├── test/ # Testes unitários e de widget
│ ├── features/
│ │ ├── auth/
│ │ │ ├── domain/
│ │ │ │ ├── entities/
│ │ │ │ │ └── user_test.dart
│ │ │ │ └── usecases/
│ │ │ │ └── sign_in_usecase_test.dart
│ │ │ └── presentation/
│ │ │ └── providers/
│ │ │ └── auth_provider_test.dart
│ │ └── [outras features]/
│ └── helpers/
│ └── test_helpers.dart # Factories de mocks compartilhados
│
└── lib/
├── main.dart # Ponto de entrada: inicializa GetIt e Firebase
│
├── app/
│ ├── app_widget.dart # MaterialApp com tema e locale
│ ├── app_router.dart # Configuração completa do go_router (Módulo 05)
│ ├── app_theme.dart # ThemeData da aplicação (Módulo 03)
│ └── app_providers.dart # MultiProvider raiz (Módulo 07)
│
├── core/
│ ├── di/
│ │ └── injection_container.dart # Composição do sistema com GetIt (Módulo 07)
│ ├── error/
│ │ ├── failures.dart
│ │ └── exceptions.dart
│ ├── network/
│ │ ├── http_client.dart
│ │ └── auth_interceptor.dart # Injetado no Módulo 11
│ ├── storage/
│ │ ├── secure_storage_service.dart # Adicionado no Módulo 11
│ │ └── preferences_service.dart # Adicionado no Módulo 08
│ ├── utils/
│ │ ├── date_formatter.dart
│ │ ├── validators.dart
│ │ └── extensions/
│ │ ├── string_extensions.dart
│ │ └── context_extensions.dart
│ ├── widgets/
│ │ ├── app_button.dart
│ │ ├── app_text_field.dart
│ │ ├── loading_overlay_widget.dart
│ │ ├── error_widget.dart
│ │ └── empty_state_widget.dart
│ └── theme/
│ ├── app_colors.dart
│ ├── app_typography.dart
│ └── app_spacing.dart
│
└── features/
│
├── auth/ # [Módulo 11] Autenticação OAuth2
│ ├── domain/
│ │ ├── entities/
│ │ │ └── user.dart
│ │ ├── value_objects/
│ │ │ └── email.dart
│ │ ├── repositories/
│ │ │ └── i_auth_repository.dart
│ │ └── usecases/
│ │ ├── sign_in_usecase.dart
│ │ └── sign_out_usecase.dart
│ ├── infrastructure/
│ │ ├── repositories/
│ │ │ └── auth_repository.dart
│ │ ├── datasources/
│ │ │ ├── auth_remote_datasource.dart
│ │ │ └── auth_local_datasource.dart
│ │ └── dtos/
│ │ └── user_dto.dart
│ └── presentation/
│ ├── providers/
│ │ └── auth_provider.dart
│ ├── pages/
│ │ ├── login_page.dart
│ │ └── splash_page.dart
│ └── widgets/
│ └── login_form.dart
│
├── home/ # [Módulo 03] Tela inicial
│ ├── domain/
│ │ └── usecases/
│ │ └── get_summary_usecase.dart
│ ├── infrastructure/
│ │ └── (implementações de dados da home)
│ └── presentation/
│ ├── providers/
│ │ └── home_provider.dart
│ ├── pages/
│ │ └── home_page.dart
│ └── widgets/
│ ├── summary_card.dart
│ └── recent_items_list.dart
│
├── [nome_dominio_principal]/ # Feature central do seu projeto
│ │ # Ex: tasks/, health/, catalog/
│ ├── domain/
│ │ ├── entities/
│ │ │ └── [entidade_principal].dart # [Módulo 02]
│ │ ├── value_objects/
│ │ │ └── (objetos de valor do domínio)
│ │ ├── repositories/
│ │ │ └── i_[dominio]_repository.dart
│ │ └── usecases/
│ │ ├── get_[dominio]_list_usecase.dart
│ │ ├── get_[dominio]_by_id_usecase.dart
│ │ ├── create_[dominio]_usecase.dart
│ │ ├── update_[dominio]_usecase.dart
│ │ └── delete_[dominio]_usecase.dart
│ ├── infrastructure/
│ │ ├── repositories/
│ │ │ └── [dominio]_repository.dart # [Módulo 09] integra local+remoto
│ │ ├── datasources/
│ │ │ ├── [dominio]_remote_datasource.dart # [Módulo 09]
│ │ │ └── [dominio]_local_datasource.dart # [Módulo 08]
│ │ └── dtos/
│ │ └── [dominio]_dto.dart
│ └── presentation/
│ ├── providers/
│ │ └── [dominio]_provider.dart # [Módulo 07]
│ ├── pages/
│ │ ├── [dominio]_list_page.dart # [Módulo 04] layout + [Módulo 05] nav
│ │ ├── [dominio]_detail_page.dart
│ │ └── [dominio]_form_page.dart # [Módulo 06] formulários
│ └── widgets/
│ ├── [dominio]_card.dart
│ └── [dominio]_list_tile.dart
│
├── notifications/ # [Módulo 12] Firebase Cloud Messaging
│ ├── domain/
│ │ ├── entities/
│ │ │ └── push_notification.dart
│ │ └── usecases/
│ │ └── handle_notification_usecase.dart
│ ├── infrastructure/
│ │ └── services/
│ │ └── fcm_service.dart # Inicialização e handlers FCM
│ └── presentation/
│ └── providers/
│ └── notification_provider.dart
│
├── native_resources/ # [Módulo 13] Câmera, GPS, sensores
│ ├── domain/
│ │ └── usecases/
│ │ ├── capture_photo_usecase.dart
│ │ └── get_location_usecase.dart
│ ├── infrastructure/
│ │ └── services/
│ │ ├── camera_service.dart
│ │ └── location_service.dart
│ └── presentation/
│ └── widgets/
│ ├── camera_capture_widget.dart
│ └── location_picker_widget.dart
│
├── settings/ # [Módulo 14] Configurações e i18n
│ ├── domain/
│ │ ├── entities/
│ │ │ └── user_settings.dart
│ │ └── usecases/
│ │ └── change_language_usecase.dart
│ ├── infrastructure/
│ │ └── repositories/
│ │ └── settings_repository.dart
│ └── presentation/
│ ├── providers/
│ │ └── settings_provider.dart
│ └── pages/
│ └── settings_page.dart
│
└── profile/ # Perfil do usuário autenticado
├── domain/
│ └── usecases/
│ └── get_user_profile_usecase.dart
├── infrastructure/
│ └── (implementações)
└── presentation/
├── providers/
│ └── profile_provider.dart
└── pages/
└── profile_page.dart
A Pasta backend — A Infraestrutura AWS
O backend do projeto é composto por funções serverless na AWS Lambda, um banco de dados relacional no RDS PostgreSQL e filas de mensagens no SQS. A organização do backend também segue princípios de separação de responsabilidades, adaptados para o contexto de funções Lambda em Python.
%%| label: pasta-backend
%%| fig-cap: Pasta backend
%%| fig-width: 11
graph TD
BE["backend/"] --> LAMBDAS["lambdas/<br/>Funções Lambda por domínio"]
BE --> DB["database/<br/>Schemas e migrações SQL"]
BE --> INFRA["infrastructure/<br/>Configurações AWS"]
BE --> SHARED["shared/<br/>Código Python compartilhado"]
BE --> TESTS_BE["tests/<br/>Testes das funções Lambda"]
LAMBDAS --> AUTH_L["auth/"]
LAMBDAS --> DOMAIN_L["[dominio_principal]/"]
LAMBDAS --> NOTIF_L["notifications/"]
AUTH_L --> AUTH_HANDLER["handler.py<br/>Ponto de entrada da Lambda"]
AUTH_L --> AUTH_DOMAIN["domain/<br/>Lógica de negócio pura"]
AUTH_L --> AUTH_INFRA["infrastructure/<br/>Conexão com BD e serviços"]
style BE fill:#fff3e0,stroke:#f57c00
style LAMBDAS fill:#ffe0b2,stroke:#e65100
style SHARED fill:#fff9c4,stroke:#f9a825
backend/
├── README.md # Instruções de configuração AWS
├── requirements.txt # Dependências Python compartilhadas
├── .env.example # Variáveis de ambiente necessárias (sem valores!)
│
├── shared/ # Código Python reutilizável entre Lambdas
│ ├── database/
│ │ └── connection.py # Pool de conexão com RDS PostgreSQL
│ ├── auth/
│ │ └── token_verifier.py # Verificação de JWT/tokens OAuth2
│ ├── models/
│ │ └── base_model.py # Classe base para modelos de domínio
│ └── utils/
│ ├── response_builder.py # Padroniza respostas HTTP das Lambdas
│ └── validators.py # Validadores reutilizáveis
│
├── database/
│ ├── schema.sql # Criação completa do schema (Módulo 10)
│ ├── seed.sql # Dados iniciais para desenvolvimento
│ └── migrations/
│ ├── 001_initial_schema.sql
│ └── 002_add_notifications_table.sql # Exemplo de migração posterior
│
├── infrastructure/
│ ├── api_gateway_config.json # Exportação da configuração do API Gateway
│ ├── sqs_config.json # Configuração das filas SQS
│ └── iam_policies/
│ ├── lambda_execution_policy.json # Política mínima para execução das Lambdas
│ └── rds_access_policy.json
│
├── tests/
│ ├── unit/
│ │ ├── auth/
│ │ │ └── test_sign_in.py
│ │ └── [dominio]/
│ │ └── test_[dominio]_usecases.py
│ ├── integration/
│ │ └── test_database_operations.py
│ └── fixtures/
│ └── sample_data.json # Dados de teste reutilizáveis
│
└── lambdas/
│
├── auth/ # [Módulo 10/11] Autenticação
│ ├── handler.py # Ponto de entrada: roteamento por evento
│ ├── domain/
│ │ ├── entities.py # Dataclasses de domínio (User, Session)
│ │ └── usecases.py # Lógica de negócio: validar token, etc.
│ ├── infrastructure/
│ │ ├── user_repository.py # Implementação com PostgreSQL
│ │ └── token_service.py # Integração com provedor OAuth2
│ └── requirements.txt # Dependências específicas desta Lambda
│
├── [dominio_principal]/ # Feature central do projeto
│ ├── handler.py # Roteamento: GET /items, POST /items, etc.
│ ├── domain/
│ │ ├── entities.py # Entidades do domínio em Python
│ │ └── usecases.py # Criar, listar, atualizar, deletar
│ ├── infrastructure/
│ │ ├── [dominio]_repository.py # Acesso ao RDS PostgreSQL
│ │ └── sqs_publisher.py # Publicação de eventos no SQS (Módulo 10)
│ └── requirements.txt
│
└── notifications/ # [Módulo 12] Envio de notificações push
├── handler.py # Acionado pelo SQS
├── domain/
│ └── usecases.py # Lógica de envio de notificação
├── infrastructure/
│ └── fcm_service.py # Firebase Admin SDK para envio FCM
└── requirements.txt
A Pasta integracao — Contrato entre Frontend e Backend
A pasta integracao é o ponto de encontro entre o frontend e o backend. É aqui que o contrato da API é definido, garantindo que ambos os lados falem a mesma língua. Quando o frontend espera que um endpoint retorne um campo chamado created_at e o backend retorna createdAt, o aplicativo quebra. O contrato de API resolve esse problema antes que ele aconteça.
integracao/
├── README.md
│
├── contracts/ # Contrato formal da API (Módulo 09)
│ ├── api-contract.md # Documentação textual dos endpoints
│ ├── openapi.yaml # Especificação OpenAPI 3.0 (opcional, avançado)
│ └── examples/
│ ├── auth_request.json # Exemplo de corpo de requisição de login
│ ├── auth_response.json # Exemplo de resposta de autenticação
│ ├── [dominio]_list_response.json # Exemplo de listagem do domínio principal
│ └── error_response.json # Formato padrão de resposta de erro
│
├── mocks/ # Servidores mock para desenvolvimento
│ ├── mock_server.py # Servidor Python que simula a API real
│ └── mock_data.json # Dados estáticos retornados pelo mock
│
└── e2e_tests/ # Testes de integração de ponta a ponta
├── test_auth_flow.py # Testa login, token e logout
└── test_[dominio]_crud.py # Testa criação, leitura, atualização e deleção
O arquivo api-contract.md deve seguir um formato padronizado. Para cada endpoint, documente o método HTTP, a URL, os headers necessários, o corpo da requisição com exemplos e todos os possíveis códigos de resposta com seus respectivos corpos. Este arquivo é a referência autoritativa que resolve qualquer desentendimento entre quem está trabalhando no frontend e quem está trabalhando no backend.
A Pasta doc — Documentação e Memória do Projeto
A documentação é a memória do projeto. Quando um novo membro chegar à equipe ou quando o professor revisitar o repositório, a pasta doc deve ser capaz de responder às perguntas mais importantes: o que este projeto faz, quais decisões foram tomadas e por quê, como o sistema está estruturado e o que aconteceu em cada semana de desenvolvimento.
doc/
├── README.md # Índice da documentação
│
├── diario/ # Diário de desenvolvimento (cada módulo)
│ ├── template_diario.md # Template fornecido pelo professor
│ ├── modulo_01.md
│ ├── modulo_02.md
│ ├── ...
│ └── modulo_15.md
│
├── arquitetura/ # Documentação de arquitetura
│ ├── visao_geral.md # Descrição textual da arquitetura
│ ├── diagrama_componentes.md # Diagrama dos componentes do sistema
│ ├── diagrama_sequencia_auth.md # Fluxo de autenticação OAuth2
│ ├── diagrama_sequencia_notificacao.md # Fluxo de envio de notificação push
│ └── diagrama_er.md # Diagrama entidade-relacionamento do BD
│
├── decisoes/ # Architecture Decision Records (ADRs)
│ ├── 001_escolha_gerenciamento_estado.md # Por que Provider e não Riverpod/Bloc
│ ├── 002_organizacao_feature_first.md # Por que Feature-First e não Layer-First
│ └── 003_[outras_decisoes].md
│
├── prototipo/ # Protótipos e esboços (Módulo 01)
│ ├── wireframes/
│ │ └── telas_principais.png # Esboço inicial das telas
│ └── fluxo_navegacao.png # Diagrama do fluxo de telas
│
└── apresentacoes/
├── entrega_parcial/
│ └── slides_entrega_parcial.pdf # Slides da apresentação parcial
└── entrega_final/
└── slides_entrega_final.pdf # Slides da apresentação final
O Arquivo README.md na Raiz
O arquivo README.md na raiz do repositório é o cartão de visita do projeto. Ele deve conter, no mínimo, o nome do projeto e uma descrição em duas ou três frases do problema que ele resolve, a lista dos membros da equipe com os nomes completos, as instruções para executar o projeto localmente (pré-requisitos, como clonar, como configurar as variáveis de ambiente e como rodar o aplicativo), e links para as partes mais importantes da documentação.
meu-projeto/ # Raiz do repositório
├── README.md # Cartão de visita: o que é e como rodar
├── .gitignore # Ignora node_modules, .env, *.keystore, etc.
│
├── frontend/ # Projeto Flutter (detalhado acima)
├── backend/ # Infraestrutura AWS (detalhado acima)
├── integracao/ # Contratos e testes E2E (detalhado acima)
└── doc/ # Documentação e diário (detalhado acima)
O Arquivo .gitignore — O Que Nunca Deve Ir para o Repositório
Atenção: arquivos sensíveis nunca devem ser commitados
Alguns arquivos jamais devem aparecer no repositório, pois contêm informações confidenciais que, se expostas publicamente, podem comprometer a segurança do projeto inteiro. O arquivo .gitignore na raiz deve incluir, no mínimo, os padrões abaixo.
# Variáveis de ambiente e segredos
.env
.env.local
.env.*.local
*.keystore
*.jks
# Configurações Firebase (contêm chaves de API)
google-services.json
GoogleService-Info.plist
# Arquivos gerados pelo Flutter
frontend/.dart_tool/
frontend/build/
frontend/.flutter-plugins
frontend/.flutter-plugins-dependencies
# Arquivos gerados pelo Python
__pycache__/
*.pyc
*.pyo
.venv/
venv/
# Arquivos de IDE
.idea/
.vscode/
*.swp
# Arquivos de sistema operacional
.DS_Store
Thumbs.dbCaso um arquivo sensível seja commitado acidentalmente, não basta deletá-lo no próximo commit — ele continuará acessível no histórico do Git. Nesse caso, o grupo deve informar o professor imediatamente para tomar as providências necessárias, como revogar as chaves expostas.
Por Que Esta Estrutura Importa
Quando você olha para essa estrutura pela primeira vez, ela pode parecer excessivamente elaborada para um projeto acadêmico. Essa é uma reação natural. Mas pense no seguinte: todo projeto começa pequeno, e projetos que não têm estrutura desde o início se tornam difíceis de manter à medida que crescem. A disciplina de seguir uma boa estrutura desde o começo é exatamente o que separa um código que funciona de um código que pode ser mantido, expandido e entregue por uma equipe profissional.
Ao longo do semestre, você vai experimentar na prática os benefícios dessa organização. Quando você precisar adicionar uma nova funcionalidade, saberá exatamente onde cada novo arquivo deve ser criado. Quando você precisar escrever testes, as dependências já estarão isoladas e os mocks serão fáceis de criar. Quando um colega de equipe modificar uma parte do projeto, as camadas bem definidas reduzirão a chance de conflitos e regressões.
E quando, no futuro, você trabalhar em um projeto profissional e reconhecer os mesmos padrões — e você vai reconhecer, porque eles são amplamente adotados pela indústria —, você já vai saber como navegar neles. Este projeto não é apenas um trabalho acadêmico: é o seu primeiro contato com a maneira como software sério é construído.
Uma Palavra Final
O Projeto Integrador é desafiador. Haverá semanas em que o código não vai funcionar como esperado, em que o time não vai conseguir chegar a um acordo sobre uma decisão, em que a integração com o backend vai apresentar erros misteriosos e em que o prazo vai parecer impossível de cumprir. Isso é completamente normal — na verdade, é exatamente assim que o desenvolvimento de software profissional funciona.
O que diferencia os melhores desenvolvedores não é a ausência de dificuldades, mas a capacidade de persistir, de pedir ajuda quando necessário, de dividir um problema grande em partes menores e de aprender com cada erro. O diário de desenvolvimento existe exatamente para capturar esses momentos: as dificuldades, as tentativas frustradas, as soluções encontradas. Ao olhar para trás ao final do semestre, você vai perceber o quanto cresceu.
Use o professor como recurso. Ele está nas aulas práticas para isso. Perguntas específicas — “estou tentando fazer X de tal forma e está dando o erro Y, já tentei Z e não funcionou” — recebem respostas muito mais úteis do que perguntas vagas. Mostre o código, explique o raciocínio, descreva o problema com precisão.
E principalmente: construa algo de que você vai se orgulhar. Este aplicativo é seu. Coloque esforço, criatividade e cuidado nele, e ele vai representar, ao final do semestre, uma prova concreta do que você é capaz de fazer.