Continuamos con nuestro proyecto de crear una aplicación en Flutter, en este punto ya tenemos claro como crear un proyecto, como emularlo y unas cuantas cosas sobre como obtener y mostrar la información. En este artículo nos centraremos en otras funcionalidades muy útiles que te serán de gran ayuda para tu aplicación. Si no has leído los anteriores artículos, te los dejo aquí abajo, si ya estás al día con esta serie de artículos, ¡comenzamos!
Cómo crear tu primera App con Flutter: Guía básica
Configuración de entorno, crear proyecto, arquitectura, funcionalidades básicas, estilos y lanzamiento.
Cómo crear tu primera App con Flutter: Guía básica 2
Widgets, navegación, visualización de datos y conexión a internet (HTTP, APIs…)
Índice de contenidos
Paquetes y plugins en Flutter
Los paquetes y plugins en Flutter son componentes reutilizables que añaden funcionalidad a nuestras aplicaciones. La diferencia principal es que los plugins pueden contener código nativo específico de plataforma.
Instalación
Para instalar paquetes y plugins, abrimos el archivo pubspec.yaml, aquí encontraremos un apartado llamado dependencies. Aquí debemos poner los paquetes y plugins que queremos instalar junto con la versión del mismo. Podemos especificar una versión específica, o una versión mínima.
dependencies:
paquete_1: ^2.3.0 # Todas las versiones superiores a 2.3.0 hasta la 3.0.0
paquete_2: '6.3.7' # Solo la versión 6.3.7
Ya tenemos definidos los paquetes o plugins a instalar, ahora ejecutamos el comando flutter pub get
y observamos la consola, aquí nos dirá si todo ha ido bien, o si hay algún problema con algún paquete o plugin.
¡Genial! Ya has instalado los paquetes y plugins que querías, ahora los puedes usar en tu código. Para usarlos deberás importarlos en tu código de la siguiente manera:
import 'package:nombre_del_paquete/nombre_del_paquete.dart';
Tabs y drawers en Flutter
En aplicaciones que usan Material Design, hay dos opciones principales para navegar, las tabs y los drawers. Estos últimos son muy útiles cuando no disponemos de espacio suficiente para las tabs.
Tabs
Para poder trabajar con tabs, es necesario tener un TabController, podemos crear uno personalizado, o usar el que viene por defecto (DefaultTabController). El TabController, es el encargado de mantener la pestaña activa y el contenido sincronizado.
Cada pestaña se crea con el widget TabBar, y su contenido con TabBarView. El uso es bastante sencillo, en la documentación oficial, podrás encontrar un ejemplo interactivo que muestra muy bien el uso de estos widgets.

return MaterialApp(
home: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: const TabBar(
tabs: [
Tab(icon: Icon(Icons.directions_car)),
Tab(icon: Icon(Icons.directions_transit)),
Tab(icon: Icon(Icons.directions_bike)),
],
),
title: const Text('Tabs Demo'),
),
body: const TabBarView(
children: [
Icon(Icons.directions_car),
Icon(Icons.directions_transit),
Icon(Icons.directions_bike),
],
),
),
),
);
Drawer
En Flutter, los drawer se crean combinando el widget Drawer con Scaffold.

static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
ListTile(
title: const Text('Home'),
selected: _selectedIndex == 0,
onTap: () {
// Update the state of the app
_onItemTapped(0);
// Then close the drawer
Navigator.pop(context);
},
),
ListTile(
title: const Text('Business'),
selected: _selectedIndex == 1,
onTap: () {
// Update the state of the app
_onItemTapped(1);
// Then close the drawer
Navigator.pop(context);
},
),
ListTile(
title: const Text('School'),
selected: _selectedIndex == 2,
onTap: () {
// Update the state of the app
_onItemTapped(2);
// Then close the drawer
Navigator.pop(context);
},
),
Una vez tenemos esta estructura creada, solo nos queda añadir contenido al drawer. Esto se podría hacer con el widget Column, pero es más recomendable usar un ListView, que permite al usuario hacer scroll en caso de tener mucho contenido. Aquí te dejo el ejemplo completo.
Almacenamiento local
El almacenamiento local es algo muy necesario en la mayoría de aplicaciones. Con Flutter es muy sencillo integrarlo. Para ello tenemos que hacer uso de la librería shared_preferences.
Una vez tenemos instalada la librería, tenemos que obtener las preferencias compartidas.
final ls = await SharedPreferences.getInstance();
¡Perfecto! Ya podemos usar el almacenamiento local. Para guardar datos, es tan sencillo como usar la función set con el tipo de dato que queremos guardar. Aquí unos ejemplos:
// Guardar un número entero llamado "numero"
await ls.setInt('numero', 5);
// Guardar un número decimal llamado "numerodec"
await ls.setDouble('numerodec', 12.3);
// Guardar un booleano
await ls.setBool('booleano', true);
// Guardar un string llamado cadena
await ls.setString('cadena', 'Hola mundo');
// Guardar una lista de Strings llamado cadenas
await ls.setStringList('cadenas', <String>['Hola', 'Mundo']);
Para recuperar datos es parecido, solo que usamos la función get en vez de set.
await ls.getInt('numero');
Para borrar los datos, usamos remove con la clave, en este caso para borrar los datos de cadenas haríamos lo siguiente:
final borrar = await ls.remove('cadenas');
Firebase para Flutter

Firebase es una plataforma BaaS (Backend-as-a-Service) de Google, que proporciona servicios como autenticación, bases de datos, machine learning y mucho más. Para usar cada uno de estos servicios, debes instalarlos individualmente.
dependencies:
firebase_core: ^1.10.0
firebase_auth: ^3.1.0
cloud_firestore: ^2.5.0
firebase_storage: ^10.2.0
La configuración es diferente para Android e iOS, recomendamos seguir paso a paso la documentación oficial para configurarlo.
Notificaciones en aplicaciones Flutter
Vamos a distinguir dos tipos de notificaciones, las notificaciones locales y las notificaciones push.
Notificaciones locales
Las notificaciones locales las genera la propia aplicación, son completamente personalizables y no requieren de conexión a internet. Esto es muy útil para alarmas, eventos o notificaciones relacionadas con el tiempo. El paquete flutter más usado para esto es flutter_local_notifications. En el siguiente código vemos como programaríamos una notificación para que avise de una reunión a las 11:

import 'package:flutter_local_notifications/flutter_local_notifications.dart';
void scheduleMeetingNotification() async {
// Configuración específica para Android
var androidDetails = AndroidNotificationDetails(
'meeting_channel_id', // ID del canal
'Meeting Notifications', // Nombre del canal
'Notificationes para reuniones programadas', // Descripción del canal
importance: Importance.high, // Importancia alta
priority: Priority.high, // Prioridad alta
);
// Configuración específica para iOS
var iosDetails = IOSNotificationDetails();
// Configuración general para ambas plataformas
var notificationDetails = NotificationDetails(
android: androidDetails,
iOS: iosDetails,
);
// Instancia del plugin
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
// Inicialización del plugin
var initializationSettingsAndroid =
AndroidInitializationSettings('@mipmap/ic_launcher');
var initializationSettingsIOS = IOSInitializationSettings();
var initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
);
await flutterLocalNotificationsPlugin.initialize(initializationSettings);
// Programar la notificación para las 11:00 AM
var scheduledTime = DateTime.now();
scheduledTime = DateTime(
scheduledTime.year,
scheduledTime.month,
scheduledTime.day,
11, // Hora
0, // Minutos
);
// Mostrar la notificación programada
await flutterLocalNotificationsPlugin.zonedSchedule(
0, // ID único de la notificación
'Reunión Programada', // Título
'No olvides tu reunión a las 11:00 AM.', // Cuerpo
scheduledTime, // Fecha y hora programadas
notificationDetails, // Detalles de la notificación
androidAllowWhileIdle: true, // Permitir mientras está inactivo
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
);
}
Notificaciones push
En el caso de las notificaciones push, no se generan en la aplicación, sino que es un servicio cloud externo quien las lanza, lo que hace que requieran de conexión a internet. Es muy útil para notificaciones en tiempo real. Su personalización es limitada, sobre todo si la aplicación se encuentra cerrada o ejecutándose en segundo plano, en estos casos los estilos de la notificación dependen del sistema operativo. El paquete con el que se gestiona esto en flutter es firebase_messaging, usando Firebase Cloud Messaging (FCM).
void _setupFirebaseMessaging() {
_firebaseMessaging = FirebaseMessaging.instance;
// Solicita permisos para notificaciones en iOS
_firebaseMessaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
// Manejo de notificaciones en segundo plano y terminadas
FirebaseMessaging.onBackgroundMessage(_firebaseBackgroundHandler);
// Escucha mensajes cuando la app está en primer plano
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Mensaje recibido en primer plano: ${message.notification?.title}');
_showNotification(message.notification?.title, message.notification?.body);
});
// Obtiene el token de FCM del dispositivo
_firebaseMessaging.getToken().then((token) {
print("Token de dispositivo: $token");
// Puedes enviar este token a tu servidor para enviar notificaciones dirigidas
});
}
// Muestra una notificación en la pantalla
void _showNotification(String? title, String? body) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(title ?? 'Nueva notificación'),
content: Text(body ?? 'Sin contenido'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('Cerrar'),
),
],
),
);
}
// Manejo de notificaciones en segundo plano
Future<void> _firebaseBackgroundHandler(RemoteMessage message) async {
print('Mensaje recibido en segundo plano: ${message.notification?.title}');
}
Gestos en apps Flutter
Los gestos se usan para que los usuarios interactúen con la aplicación, vamos a ver cuáles son estos gestos y cómo los podemos manejar.
Los gestos más comunes son los siguientes:
- Tap: tocar la superficie del dispositivo con la yema del dedo durante un breve período de tiempo y finalmente soltar el dedo.
- Double Tap: tocar dos veces en un período corto.
- Drag: Tocar la superficie del dispositivo con la yema del dedo y luego mover el dedo de manera constante y finalmente soltar.
- Flick: Similar a arrastrar, pero haciéndolo de forma más rápida.
- Pinch: Pellizcar la superficie del dispositivo utilizando dos dedos, como si hiciésemos zoom-out.
- Zoom: Lo opuesto de pellizcar.
- Panning: tocar la superficie del dispositivo con la yema del dedo y moverlo en la dirección deseada sin soltar el dedo.
Cada gesto tiene asociados ciertos eventos, por ejemplo el gesto tap tiene los eventos onTapDown, onTapUp, onTap y onTapCancel. Con estos eventos y con ayuda del widget GestureDetector, podemos gestionar las acciones de los gestos en nuestra aplicación.
child: GestureDetector(
onTap: () => _updateMessage("Tap detectado"),
onDoubleTap: () => _updateMessage("Double Tap detectado"),
onLongPress: () => _updateMessage("Long Press detectado"),
onPanUpdate: (details) => _updateMessage(
"Panning detectado: dx=${details.delta.dx}, dy=${details.delta.dy}"),
onScaleStart: (_) => _updateMessage("Pinch/Zoom detectado"),
child: Container(
width: 200,
height: 200,
color: Colors.blueAccent,
child: Center(
child: Text(
"Toca aquí",
style: TextStyle(color: Colors.white, fontSize: 18),
),
),
),
),
Además de GestureDetector, en Flutter tenemos otros widgets para gestionar gestos más específicos:
- Dismissible: admite el gesto de deslizar para finalizar el widget.
- Draggable: admite el gesto de arrastrar para hacer que el widget sea móvil.
- LongPressDraggable: si los widgets principales se pueden arrastrar, admite el gesto de arrastrar para mover el widget secundario.
- DragTarget: acepta cualquier widget arrastrable
- IgnorePointer: cuando se usa, oculta el widget y sus elementos secundarios correspondientes del proceso de detección de gestos.
- AbsorbPointer: detiene el proceso de detección de gestos para evitar el envío de acciones a widgets superpuestos.
- Scrollable: hace que el contenido disponible dentro del widget sea desplazable.
Integración de Flutter con código nativo
Flutter ofrece una poderosa capacidad de integración con código nativo, permitiéndonos aprovechar funcionalidades específicas de plataforma y APIs nativas. Esta integración se puede lograr de varias maneras:

Método de plataforma
Los métodos de plataforma permiten la comunicación entre el código Dart de Flutter y el código nativo. Se implementan utilizando MethodChannel:
const platform = MethodChannel('com.example/native');
try {
final result = await platform.invokeMethod('getNativeData');
// Usar el resultado
} on PlatformException catch (e) {
// Manejar errores
}
FFI (Foreign Function Interface)
FFI permite llamar directamente a funciones de bibliotecas nativas desde Dart:
import 'dart:ffi' as ffi;
final dylib = ffi.DynamicLibrary.open('path/to/native/library');
final multiply = dylib.lookupFunction<ffi.Int32 Function(ffi.Int32, ffi.Int32), int Function(int, int)>('multiply');
int result = multiply(3, 4);
Vistas de plataforma
Las vistas de plataforma permiten incrustar vistas nativas directamente en la interfaz de usuario de Flutter:
AndroidView(
viewType: 'native-view',
creationParams: {'param1': 'value1'},
creationParamsCodec: StandardMessageCodec(),
)
¡Ya lo tienes!
¡Genial! Ya dominas muchas de las funcionalidades que nos ofrece Flutter. En este artículo hemos visto funcionalidades muy interesantes y útiles. ¿Cuál ha sido tu favorita? ¡Déjanoslo en comentarios!
Deja una respuesta
Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *