Entendendo nosso primeiro projeto Flutter

Na aula anterior, criamos nosso primeiro projeto Flutter. Ele é muito simples, contendo apenas um botão que incrementa um número ao ser clicado. Veja abaixo a estrutura de pastas deste projeto.

Estrutura de pastas do projeto

Você verá diversas pastas e arquivos como android, web, windows, lib, test e outros. Onde fica o código fonte que você vai escrever? No android? No windows? Na verdade não, fica na pasta lib. Ao abrir a lib, você verá um arquivo main.dart.

A pasta lib vai guardar todo o seu código fonte daqui em diante. Você vai programar em Dart, não em Java nem C++. O Flutter usa a linguagem Dart. Mas por que existem pastas como android, web e windows?

A explicação é simples: o Flutter consegue compilar para aplicativos de várias plataformas graças a essas pastas.

O Flutter é um framework com um único código Dart que roda em múltiplas plataformas. A pasta lib contém o código de negócio compartilhado entre todas as plataformas que você escreve. Já as pastas android / windows / web / macos / linux são projetos nativos de hospedagem de cada plataforma, responsáveis por embutir o Flutter no sistema correspondente para execução.

1. Pasta android

Trata-se do projeto nativo da plataforma Android

Ele fornece o contêiner de execução do app Android e hospeda o motor de renderização do Flutter. Todas as configurações de baixo nível relacionadas ao Android são feitas aqui: permissões do app, ícones, nome de pacote e mais. Ou seja, aqui você configura elementos exclusivos do Android e consegue chamar APIs nativas do sistema (câmera, bluetooth, notificações, localização etc.)

2. Pasta windows

É o projeto nativo para desktop Windows

Trata-se de um projeto nativo C++ Win32 que cria janelas do Windows e inicializa o motor de renderização Skia. Skia é a biblioteca gráfica do Google, utilizada pelo Flutter para renderizar janelas e todos os controles em apps de desktop.

Dentro da pasta windows ficam as configurações de baixo nível do software de desktop Windows: tamanho da janela, barra de título, ícones, bandeja do sistema (o ícone no canto inferior direito do Windows).

No final você consegue empacotar o projeto em arquivos exe ou msix para publicar na Microsoft Store, entre outras opções.

3. Pasta web

Projeto de página web estática (HTML/JS/CSS) com o ponto de entrada web index.html. Seu código Dart será compilado em recursos para web.

Permite configurar título da página, ícones, recursos estáticos e rotas. Ao compilar, gera arquivos HTML/CSS/JS prontos para hospedar em um servidor e abrir em navegadores.

Outras pastas

Não usamos iOS, macOS nem Linux neste exemplo, pois não marcamos essas opções na criação do projeto, por isso essas pastas não aparecem aqui. Mas o funcionamento delas é igual.

ios: Projeto nativo Xcode para iOS da Apple, usado para empacotar arquivos ipa e lançar no iPhone

macos: Projeto nativo Cocoa para desktop macOS, cliente para computadores Mac

linux: Projeto de desktop Linux baseado em GTK

No canto superior direito do Android Studio você seleciona diferentes plataformas para compilar versões distintas do aplicativo. Você também pode usar a linha de comando diretamente; na verdade, ao escolher uma plataforma e clicar no triângulo verde no Android Studio, ele executa comandos do Flutter nos bastidores.

# Rodar versão Android, lê configurações da pasta android
flutter run -d android

# Rodar desktop Windows, lê o projeto C++ da pasta windows
flutter run -d windows

# Rodar versão web, lê o html da pasta web
flutter run -d chromeCode language: PHP (php)

Abra o arquivo runner / main.php da pasta windows

#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <windows.h>

#include "flutter_window.h"
#include "utils.h"

int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
                      _In_ wchar_t *command_line, _In_ int show_command) {
  // Attach to console when present (e.g., 'flutter run') or create a
  // new console when running with a debugger.
  if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
    CreateAndAttachConsole();
  }

  // Initialize COM, so that it is available for use in the library and/or
  // plugins.
  ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);

  flutter::DartProject project(L"data");

  std::vector<std::string> command_line_arguments =
      GetCommandLineArguments();

  project.set_dart_entrypoint_arguments(std::move(command_line_arguments));

  FlutterWindow window(project);
  Win32Window::Point origin(10, 10);
  Win32Window::Size size(1280, 720);
  if (!window.Create(L"myproject", origin, size)) {
    return EXIT_FAILURE;
  }
  window.SetQuitOnClose(true);

  ::MSG msg;
  while (::GetMessage(&msg, nullptr, 0, 0)) {
    ::TranslateMessage(&msg);
    ::DispatchMessage(&msg);
  }

  ::CoUninitialize();
  return EXIT_SUCCESS;
}
Code language: PHP (php)

Se você compilar para o Windows Desktop, esse main.cpp é o ponto de entrada real do programa: esse arquivo C++ é executado primeiro, depois o seu lib/main.dart.

Da mesma forma, ao abrir a pasta android você verá uma estrutura quase idêntica aos projetos Android desenvolvidos com Kotlin ou Java.

Também contém a pasta src, o arquivo AndroidManifest.xml, MainActivity e outros elementos familiares para quem desenvolve apps Android. De fato, ao criar um app Android com Flutter, a execução começa por essa pasta android.

Da mesma maneira, ao abrir a pasta web você encontra o ponto de entrada web index.html

Arquivos Dart

O arquivo main.dart dentro da lib será o local onde você fará a maior parte do desenvolvimento. Você pode criar subpastas e adicionar outros arquivos Dart dentro da lib.

Abra o main.dart

// Importar biblioteca de componentes Material
import 'package:flutter/material.dart';

// Função de entrada do programa
void main() {
  runApp(const MyApp());
}

// Componente raiz do aplicativo (sem estado)
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      // Configuração global do tema
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      // Página inicial
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

// Página inicial (componente com estado)
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  // Parâmetro de título da página
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

// Classe de gerenciamento de estado da página inicial
class _MyHomePageState extends State<MyHomePage> {
  // Variável contador
  int _counter = 0;

  // Método para incrementar o contador
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // Modelo base de layout de página
    return Scaffold(
      // Barra de navegação superior
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      // Conteúdo principal da página
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('You have pushed the button this many times:'),
            Text(
              '$_counter', // Exibe a variável numérica acima
              style: Theme.of(context).textTheme.headlineMedium, // Estilo do texto
            ),
          ],
        ),
      ),
      // Botão flutuante inferior direito: onPressed executa _incrementCounter, tooltip é o texto de dica, child configura o ícone
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}Code language: JavaScript (javascript)

1. Parte de entrada

import 'package:flutter/material.dart';
void main() {
  runApp(const MyApp());
}
Code language: JavaScript (javascript)
  • Importa o Material, o conjunto oficial de componentes UI do Google (botões, telas, textos etc.). A Apple tem seu próprio framework de interface.
  • main é o único ponto de entrada de um programa Dart; runApp() inicializa o app e monta o componente raiz MyApp

2. MyApp: Componente raiz global

Ele é um StatelessWidget (componente sem estado), vamos explicar depois, basta saber isso por enquanto.

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // Componente raiz do seu aplicativo
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}
Code language: JavaScript (javascript)
  • Configurações globais: Nome do app, tema geral, página inicial
  • MaterialApp: Contêiner de aplicativo com estilo Material, gerencia rotas, temas e títulos de forma unificada
  • home define a página padrão ao abrir o app: MyHomePage

3. MyHomePage: Página inicial

Página exibida por padrão ao abrir a janela ou o app, é um StatefulWidget (componente com estado), detalharemos depois.

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}Code language: JavaScript (javascript)
  • Contém dados variáveis (o número do contador), por isso usamos um componente com estado
  • Recebe o parâmetro title de fora e é lido via widget.title na classe de estado
  • Deve ser acompanhado da classe _MyHomePageState, responsável por armazenar variáveis e lógicas de interação da tela

4. _MyHomePageState

Classe de lógica de estado, responsável por processar lógica, renderizar e atualizar a página

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(

        backgroundColor: Theme.of(context).colorScheme.inversePrimary,

        title: Text(widget.title),
      ),
      body: Center(

        child: Column(

          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('You have pushed the button this many times:'),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}Code language: JavaScript (javascript)
  1. int _counter = 0: Dado variável da página: valor do contador
  2. _incrementCounter(): Evento ao clicar no botão
    • setState() avisa ao Flutter que os dados mudaram e a interface deve ser redesenhada
    • Incrementa o contador em +1
  3. build(): Executado toda vez que houver atualização, retorna todo o layout da página

5. Scaffold: Modelo de página

Modelo de layout de página, pertence à classe _MyHomePageState

  • appBar: Barra de título superior
  • body: Conteúdo principal da página
    • Center centraliza os elementos, Column organiza os textos verticalmente
  • floatingActionButton: Botão circular de mais no canto inferior direito, vinculado ao método de incremento no clique

Fluxo completo de execução

  1. Início do programa → executa main → carrega MyApp
  2. MyApp renderiza MaterialApp → carrega a página inicial MyHomePage
  3. A página renderiza barra de título, textos centralizados e botão de soma
  4. Clique no botão → executa _incrementCounter + setState redesenha a tela → número atualizado

Entendendo nosso primeiro projeto Flutter

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *