Comprender nuestro primer proyecto Flutter

En la lección anterior creamos nuestro primer proyecto Flutter, es un proyecto muy simple que solo contiene un botón que incrementa un número al pulsarlo. A continuación se muestra la estructura de carpetas de este proyecto.

Estructura de carpetas del proyecto

Verás muchas carpetas y archivos como android, web, windows, lib, test, etc. ¿Dónde está el código fuente que debes escribir? ¿En android? ¿En windows? En realidad no, está en la carpeta lib. Al abrir lib verás un archivo main.dart.

La carpeta lib almacenará todo tu código fuente en adelante. Escribirás código en Dart, no en Java ni C++. Flutter utiliza el lenguaje Dart. ¿Por qué existen entonces las carpetas android, web, windows y demás?

Es fácil de entender: Flutter puede compilarse en aplicaciones multiplataforma gracias a estas carpetas.

Flutter es un framework de un único código Dart para ejecución en múltiples plataformas. La carpeta lib contiene el código de negocio común multiplataforma que escribes; mientras que las carpetas android / windows / web / macos / linux son proyectos nativos de cada plataforma anfitriona, encargados de integrar Flutter en el sistema correspondiente para su ejecución.

1. Carpeta android

Se trata del proyecto nativo de la plataforma Android

Proporciona el contenedor de ejecución de la app Android y aloja el motor de renderizado de Flutter. Aquí se configuran todos los ajustes de bajo nivel relacionados con Android: permisos de la app, iconos, nombre de paquete, etc. Es decir, aquí puedes configurar elementos exclusivos de Android y llamar a APIs nativas del sistema (cámara, bluetooth, notificaciones, ubicación, etc.)

2. Carpeta windows

Corresponde al proyecto nativo de escritorio Windows

Es un proyecto nativo C++ Win32 que crea ventanas del sistema Windows e inicializa el motor de renderizado Skia. Skia es la librería gráfica de Google, utilizada por Flutter para renderizar ventanas y controles en aplicaciones de escritorio.

Los archivos dentro de la carpeta windows gestionan la configuración de bajo nivel del software de escritorio Windows: tamaño de ventana, barra de título, iconos, bandeja del sistema (el icono en la esquina inferior derecha de Windows).

Finalmente puedes empaquetar el proyecto en formatos exe o msix para publicarlo en Microsoft Store, entre otros.

3. Carpeta web

Proyecto de página web estática (HTML/JS/CSS) que proporciona el punto de entrada web index.html. Tu código Dart se compilará finalmente en recursos web.

Permite configurar el título de la página, iconos, recursos estáticos y rutas. Al compilar genera archivos HTML/CSS/JS listos para desplegar en un servidor y abrir en cualquier navegador.

Otros directorios

No usamos iOS, macOS ni Linux en este ejemplo porque no marcamos esas opciones al crear el proyecto, por lo que esas carpetas no aparecen aquí. Sin embargo, su funcionamiento es idéntico.

ios: Proyecto nativo Xcode de Apple iOS, para empaquetar archivos ipa y publicar en iPhone

macos: Proyecto nativo Cocoa para escritorio macOS, cliente para ordenadores Mac

linux: Proyecto de escritorio Linux basado en GTK

En la esquina superior derecha de Android Studio puedes seleccionar distintas plataformas para compilar versiones diferentes de la aplicación. También puedes usar la línea de comandos directamente; de hecho, al elegir una plataforma y pulsar el triángulo verde en Android Studio, se ejecutan internamente comandos de Flutter.

# Ejecutar versión Android, lee configuraciones de la carpeta android
flutter run -d android

# Ejecutar escritorio Windows, lee el proyecto C++ de la carpeta windows
flutter run -d windows

# Ejecutar versión web, lee el archivo html de la carpeta web
flutter run -d chromeLenguaje del código: PHP (php)

Abre el archivo runner / main.cpp dentro de la carpeta 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;
}
Lenguaje del código: PHP (php)

Si compilas para Windows Desktop, este main.cpp es el punto de entrada real del programa: se ejecuta primero este archivo C++ y luego pasa a tu lib/main.dart.

Del mismo modo, al abrir la carpeta android verás una estructura casi idéntica a los proyectos Android desarrollados con Kotlin o Java.

También contiene la carpeta src, el archivo AndroidManifest.xml, MainActivity y otros elementos que te resultarán familiares si has desarrollado apps Android. En efecto, al crear una app Android con Flutter, la ejecución comienza desde esta carpeta android.

Igualmente, al abrir la carpeta web encontrarás el punto de entrada web index.html

Archivos Dart

El archivo main.dart dentro de lib será el lugar donde realizarás la mayor parte del desarrollo. Puedes crear subcarpetas y añadir otros archivos Dart dentro de lib.

Abre main.dart

// Importar librería de componentes Material
import 'package:flutter/material.dart';

// Función de entrada del programa
void main() {
  runApp(const MyApp());
}

// Componente raíz de la aplicación (sin estado)
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      // Configuración global del tema
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      // Página principal
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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

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

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

// Clase de gestión de estado de la página principal
class _MyHomePageState extends State<MyHomePage> {
  // Variable contador
  int _counter = 0;

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

  @override
  Widget build(BuildContext context) {
    // Plantilla base de layout de página
    return Scaffold(
      // Barra de navegación superior
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      // Contenido principal de la página
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('You have pushed the button this many times:'),
            Text(
              '$_counter', // Muestra la variable numérica anterior
              style: Theme.of(context).textTheme.headlineMedium, // Estilo de texto
            ),
          ],
        ),
      ),
      // Botón flotante inferior derecho: onPressed ejecuta _incrementCounter, tooltip es el texto de ayuda, child configura el icono
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}Lenguaje del código: JavaScript (javascript)

1. Sección de entrada

import 'package:flutter/material.dart';
void main() {
  runApp(const MyApp());
}
Lenguaje del código: JavaScript (javascript)
  • Importa Material, el conjunto oficial de componentes UI de Google (botones, páginas, textos, etc.). Apple cuenta con su propio framework de interfaz.
  • main es el único punto de entrada de un programa Dart; runApp() inicia la aplicación y monta el componente raíz MyApp

2. MyApp: Componente raíz global

Se trata de un StatelessWidget (componente sin estado), lo explicaremos más adelante, por ahora basta con saberlo.

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

  // Componente raíz de tu aplicación
  @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'),
    );
  }
}
Lenguaje del código: JavaScript (javascript)
  • Configuración global: Nombre de la app, tema general, página inicial
  • MaterialApp: Contenedor de aplicaciones con estilo Material, gestiona rutas, temas y títulos de forma unificada
  • home define la página predeterminada al abrir la app: MyHomePage

3. MyHomePage: Página principal

Página que se muestra por defecto al abrir la ventana o la app, es un StatefulWidget (componente con estado), lo veremos con detalle después.

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}Lenguaje del código: JavaScript (javascript)
  • Al contener datos variables (el número del contador), se usa un componente con estado
  • Recibe el parámetro title desde fuera y se lee mediante widget.title en la clase de estado
  • Debe ir acompañado de una clase _MyHomePageState dedicada a almacenar variables y lógica de interacción de la página

4. _MyHomePageState

Clase de lógica de estado, encargada de gestionar la lógica, renderizado y actualizaciones de la 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),
      ),
    );
  }
}Lenguaje del código: JavaScript (javascript)
  1. int _counter = 0: Dato variable de la página: valor del contador
  2. _incrementCounter(): Evento al pulsar el botón
    • setState() avisa a Flutter que los datos han cambiado y debe refrescar la interfaz de usuario
    • Incrementa el contador en 1
  3. build(): Se ejecuta en cada refresco y devuelve el layout completo de la página

5. Scaffold: Plantilla de página

Plantilla de layout de página, pertenece a la clase _MyHomePageState

  • appBar: Barra de título superior
  • body: Contenido principal de la página
    • Center centra los elementos, Column organiza los textos en vertical
  • floatingActionButton: Botón circular con signo más en la esquina inferior derecha, vinculado al método de incremento al pulsar

Flujo general de ejecución

  1. Inicio del programa → ejecución de main → carga de MyApp
  2. MyApp renderiza MaterialApp → carga la página inicial MyHomePage
  3. La página renderiza la barra de título, los textos centrados y el botón de suma
  4. Al pulsar el botón → se ejecuta _incrementCounter + setState redibuja la página → el número se actualiza

Comprender nuestro primer proyecto Flutter

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *