Flutter Widget

De MediaWiki
Revisión del 18:10 10 abr 2026 de Angelfg (discusión | contribuciones) (→‎Añadiendo nuevos widgets)
(dif) ← Revisión anterior | Revisión actual (dif) | Revisión siguiente → (dif)
Ir a la navegación Ir a la búsqueda

Widget. Conceptos básicos

  • Más información en:
  • Acabaréis hasta las cejas de esta frase si buscáis información sobre Flutter:Todo en Flutter son widgets.
Un widget no es más que un componente que se va a 'dibujar' de una determinada forma (con un contenido y forma determinado por nosotros) y tiene un estado.
Pero el widget en si mismo no sólo es un componente gráfico, es un 'objeto' en el que mediante propiedades, indicamos cómo tiene que dibujarse y qué tiene que dibujarse
Ejemplos de Widgets:
Flutter dart widget 1.JPG
En la imagen anterior, vemos:
  • Un menú
  • Un título de la aplicación
  • Un texto
  • Un botón
Veremos posteriormente que existen widgets que no vemos pero que nos sirven para colocar los widget que sí vemos donde queramos.
Por ejemplo, el texto está centrado en la mitad de la pantalla. Esto se consigue con otro Widget (Center).



La primera pregunta que se viene a la cabeza con esa definición es: ¿ qué es un estado ?
Digamos que un estado lo componen las características del widget. Tanto las gráficas (por ejemplo, cambiar el color del texto) como las específicas del widget, como por ejemplo, en una caja de texto, el texto escrito, o en una lista, el elemento seleccionado.
  • Existen dos tipos de Widgets:
  • Stateful Widget: guarda su estado
  • Stateless Widget: no guarda su estado.
Todos los widgets deriva de una de estas dos clases (que son clases abstractas).
Digamos que cuando un elemento gráfico 'cambia', debido por ejemplo a iteracciones del usuario con la aplicación, es necesario 'actualizar' gráficamente dicho componente.
En ese caso, cambiamos 'su estado' y por lo tanto tiene que ser un Statefull Widget.
Un Stateful Widget se va a poder 'redibujar' a si mismo cuando se produzca un cambio que queramos reflejar gráficamente (veremos que existe una sentencia que provaca que se redibuje).
También podemos necesitar emplear un Stateful Widget cuando queramos guardar el dato en algún atributo, por ejemplo, un formulario, en el que dentro de una caja de texto escribimos nuestro nombre y queramos hacer algún tipo de gestión con esa información.


  • En la aplicación que se crea al incio de un proyecto Flutter aparece un contador.
Al pulsar el botón '+' el contador se incrementa. Es necesario por tanto 'redibujar' el contador (cambia de número). Por tanto su estado cambia y por tanto es un StateFul Widget.
Flutter dart widget 2.JPG



Aquellas partes de la pantalla que permanecen 'inalterables' una vez cargados los datos son considerados StateLess Widget.



Veamos en ejemplo sencillo de un Stateless Widget:

Nota:
  • Si estáis en el Android Studio u otro IDE, es necesasario crear un nuevo proyecto Flutter (ahora no estamos creando sólo ficheros DART).
  • Si os aparece el siguiente error en el IDE: Target of URI doesn't exist: 'package:flutter/material.dart', debéis abrir una consola y escribe la siguiente línea: flutter packages get desde el directorio raíz del proyecto Flutter.
import 'package:flutter/material.dart';

void main() {
  runApp(
    const Center(
      child: Text(
        'HOLA MUNDO',
        textDirection: TextDirection.ltr,
        style: const TextStyle(fontWeight: FontWeight.bold, 
                               color: Colors.red)
      ),
    ),
  );
}
Flutter dart widget 6.JPG



Sólamente fijarse en el código del Widget (ya explicaremos posteriormente lo demás).
  • Si os dáis cuenta, los widgets se 'anidan' unos dentro de otros (como contenedores 'div' de html).
  • El widget Center es un contenedor que se expande todo lo que pueda.
Lo que estamos haciendo realmente es crear un objeto de una clase Center. Lo anterior es lo mismo que si escribimos: new Center (....) (recordar que en Dart es lo mismo poner new Clase() que Clase())
Esta clase, en su constructor espera recibir los siguientes datos:
Flutter dart widget 3.JPG
  • Como vemos son parámetros con nombre (van entre llaves)
  • Admiten valores nulos, por lo tanto, no es obligatorio emplearlos.
En el ejemplo, usamos el parámetro child que espera recibir como dato....sí, un widget. Por eso creamos un nuevo widget Text (es lo mismo poner Text (...) que new Text(...).
Flutter dart widget 5.JPG
  • Como vemos espera recibir un parámetro posicional (un String que sería el texto a mostrar) y el resto de parámetros son parámetros con nombre todos optativos.
  • Los parámetros con nombre son todos optativos, por lo que no es necesario enviarlos.




Diseño de pantallas

  • En Flutter, para diseñar una pantalla se hacen uso de unos Widget´s especiales que sirven para indicar cómo se van a colocar los widget´s que se encuentren en su interior.
De tal forma que al final tendremos una anidación de Widget´s unos dentro de otros hasta conformar el diseño gráfico de la pantalla.
Flutter dart widget 7.JPG
Imagen obtenida de este enlace

Veamos un ejemplo concreto:

Flutter dart widget 8.JPG
Imagen obtenida de este enlace

Y ahora analicemos la parte izquierda de la pantalla:

Flutter dart widget 9.JPG
Imagen obtenida de este enlace




Creando una aplicación Flutter con un Widget

  • En este punto vamos a explicar como crear una aplicación Flutter.
  • Como en todo lenguaje, cuando se ejecuta una aplicación se necesita un punto de entrada a la misma, es decir, una función main().
  • Flutter, dentro de dicha función va a necesitar que se llame a una función denominada runApp().
  • Esta función (runApp) está definida en paquete: package:flutter/material.dart. Necesitaremos por tanto importar dicho paquete.
  • La función runApp() espera recibir como argumento un Widget, por lo que crearemos una clase que derive de un StatelessWidget.


import 'package:flutter/material.dart';

void main() => runApp(MiWidget());


class MiWidget extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Center(
      child: Text(
        'Hola Mundo',
        textDirection: TextDirection.ltr,
      ),
    );
  }

}



Widget MaterialApp

  • Este Widget lo vamos a necesitar siempre como 'widget inicial' de nuestra aplicación Android, la cual va a hacer uso del Material Design.
Gracias a él podemos determinal el aspecto (theme) que van a tener el resto de Widget de la pantalla.
Nota: En este Widget podemos desactivar el banner que aparece en la parte superior, indicando que estamos en modo depuración, con la propiedad: debugShowCheckedModeBanner
Un ejemplo de código:
import 'package:flutter/material.dart';

void main() => runApp(
    MaterialApp(
      home: Text('Texto del Material App'),
      debugShowCheckedModeBanner: false,
    )
);
Flutter dart widget 11.JPG
Como vemos el resultado no es muy espectacular :)


Ejercicio propuesto: Crea un nuevo StatelessWidget y haz que devuelva un widget MaterialApp. Haz que la función runApp cargue dicho widget.


  • Veremos a continuación como este Widget se combina normalmente con un widget Scaffold, el cual nos va a permitir añadir a nuestra aplicación un Appbar y otra serie de elementos comunes a las aplicaciones.




Widget Scafold


  • Es un Widget que implementa lo básico para una pantalla de Android.
  • Puede tener una AppBar
  • Puede tener un ButtonNavigationBar
  • Puede tener un Drawer (menú lateral).
  • Puede tener un Float Action Button
Flutter dart widget 10.JPG

Nota: Empleando este widget, al añadir widget´s de tipo Text no es necesario indicar la orientación del texto.


  • Como comenté anteriormente, este Widget suele ir con el widget MaterialApp.
Veamos un ejemplo de uso:
import 'package:flutter/material.dart';

void main() => runApp(MiMaterialApp());

class MiMaterialApp extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return  MaterialApp(
      home: MiWidget(),
      debugShowCheckedModeBanner: false,
    );
  }
}


class MiWidget extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text('Título del AppBar'),
        centerTitle: true,
      ),
      body: Center(
        child: Text('Cuerpo del body'),
      ),
    );
  }

}
Flutter dart widget 12.JPG


Como vemos es un poco lioso (Flutter es así, que le vamos a hacer :) )
  • Primero tenemos un método main que llama al método runApp. Dicho método espera un Widget. Le pasamos una instancia de la clase MiMaterialApp.
  • La clase MiMaterialApp (que es un StatelessWidget) devuelve (método build) un Widget que es un objeto de la clase MaterialApp. Dicha clase me permite inhabilitar el banner de debugger y tiene como parámetro el Widget que va a visualizar como contenido. Dicho widget es un objeto de la clase MiWidget.
  • La clase MiWidget (que es un StatelessWidget) devuelve (método build) un objeto de la clase Scaffold el cual permite establecer, entre otras muchas cosas, una AppBar y un contenido.



  • NOTA:
  • Existen unas guías de diseño (Google Material Design) que son definidas por Goole para dar un aspecto homogéneo a las aplicaciones: https://m3.material.io/
Actualmente (Noviembre 2024) van por la versión 3.
En esta nueva versión la AppBar del Scaffold no aparece con un color diferente. Para solucionarlo podemos poder en background-color en la AppBar o decirle a MaterialApp que emplee la versión 2 de Material de la siguiente forma:
class MiMaterial extends StatelessWidget {
const MiMaterial({ super.key });

  @override
  Widget build(BuildContext context){
    return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomeScreen(),
        theme: ThemeData(                 // TAMBIEN VALDRIA:  theme: ThemeData.light(useMaterial3: true)
          useMaterial3: false
        ),
    );
  }
}


  • Como comenté antes, en el MaterialApp podemos definir el aspecto que tendrá nuestra aplicación. En Android existen dos themes principales: dark y light.
Podemos establecer en el MeterialApp cual de ellos se va a usar.
class MiMaterial extends StatelessWidget {
const MiMaterial({ super.key });

  @override
  Widget build(BuildContext context){
    return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomeScreen(),
        theme: ThemeData.light(),    // O bien ThemeData.dark()
    );
  }
}


Al aplicar ese theme automáticamente va a aplicar un estilo de diseño a todos widgets visuales que se empleen.
Podremos sobreescribir parte del estilo que trae por defecto (aunque no se recomienda, ya que puede que no sigas las normas de diseño de MaterialDesign).
Por ejemplo:
class MiMaterial extends StatelessWidget {
const MiMaterial({ super.key });

  @override
  Widget build(BuildContext context){
    return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomeScreen(),
        theme: ThemeData.light().copyWith(
          textTheme: TextTheme(titleMedium: TextStyle(color: Colors.blue,fontSize: 40))
        ),
    );
  }
}
En este caso estoy cambiando el estilo por defecto del titleMedium aplicado a textos.
Si ahora empleo ese estilo en un texto del scafold aplicará dicho estilo modificado:
class MiPantalla extends StatelessWidget {
const MiPantalla({ super.key });

  @override
  Widget build(BuildContext context){
    return Scaffold(
      appBar: AppBar(
        title: const Text('Mi Pantalla'),
        titleTextStyle: TextStyle(color: Colors.indigo,fontSize: 20),
        centerTitle: true,
      ),
      body: Center(child: Text('OLA MUNDO',
          style: Theme.of(context).textTheme.titleMedium,)),
      
    );
  }
}



Estructura de páginas

  • En este vídeo muestro como crear una estructura de directorios y ficheros dentro de un proyecto Flutter, donde guardaremos las diferentes páginas que conformarán nuestro proyecto.
Conoceremos como importar los ficheros donde se encuentran las diferentes páginas.


  • Haremos una variación en la estructura de directorios y intentaremos crear la siguiente:
lib/
│
├── main.dart                # Archivo principal que ejecuta la aplicación
├── core/
│   ├── constants/           # Definiciones constantes, como colores, tamaños, etc.
│   ├── utils/               # Utilidades y funciones compartidas
│   ├── widgets/             # Widgets reutilizables en toda la app (componentes compartidos)
│   └── material_app.dart     # Clase en la que se cargan las rutas definidas en app_routes.dart, se establece el theme y se indica la ruta incial de carga.
│
├── models/                  # Modelos de datos (opcional, si usas un patrón basado en datos)
│
├── services/                # Servicios, APIs, o cualquier capa lógica de negocio (opcional)
│
├── routes/                  # Archivo(s) para gestionar las rutas de la aplicación
│   └── app_routes.dart      # Definir rutas estáticas y la navegación
│
└── providers/               # Directorio donde se guardarán los providers que sirven de puente entre los datos y los widgets que van a emplear dichos datos.
│
└── screens/
    ├── home/                # Directorio de la pantalla principal (Home)
    │   ├── home_screen.dart # Pantalla Home
    │   └── home_widgets.dart# Widgets específicos de la pantalla Home (opcional). Si no se va a crear podemos crear un único archivo home_screen.dart y no crear el directorio
    │
    ├── login/               # Directorio de la pantalla de inicio de sesión
    │   ├── login_screen.dart# Pantalla de login
    │   └── login_widgets.dart# Widgets específicos de login (opcional)
    │
    └── settings/            # Directorio para la pantalla de configuración
        ├── settings_screen.dart
        └── settings_widgets.dart


  • Fichero: home_screen.dart.
Se encuentra en el directorio lib/screens/
import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Título del AppBar2'),
        centerTitle: true,
      ),
      body: const Center(
        child: Text('Cuerpo del body2'),
      ),
    );
  }
}
  • Fichero mi_material_app.dart.
Se encuentra en el directorio lib/core/
import 'package:flutter/material.dart';
import 'package:flutter_desenho/screens/home_screen.dart';

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

  @override
  Widget build(BuildContext context) {
    return  const MaterialApp(
      home: HomeScreen(),
      debugShowCheckedModeBanner: false,
    );
  }
}
  • Fichero main.dart.
Se encuentra en el directorio lib
import 'package:flutter/mi_material.dart';
import 'core/material_app.dart';

void main() => runApp(const MiMaterialApp());




Añadiendo nuevos widgets

  • En Flutter existen muchos widgets ya hechos por programadores y que se ponen a disposición de los usuarios.
También existen paquetes que aumentan la funcionalidad de nuestro proyecto (por ejemplo, tenemos el paquete firebase messaging que nos permite usar el servicio de Firebase para enviar mensajes a dispositivos usando FCM (Firebase Cloud Messaging).


En dicho sitio podemos consultar cuales son los widget o paquetes más populares o buscar por un widget o paquete concreto.
  • Vamos a ver un ejemplo de como integrar un nuevo widget a nuestro proyecto.
Supongamos que queremos emplear un widget que nos permita mostrar mensajes informativos a un usuario de nuestra aplicación.





Enlace a la página principal de la UD3

Enlace a la página principal del curso




-- Ángel D. Fernández González -- (2021).