certmundo.
es‑mx

6 min de lectura

¿Cómo guardar datos localmente en una app móvil?

Guardar datos localmente significa almacenar información directamente en el dispositivo del usuario, sin necesitar internet.

¿Sabías que el 60% de los usuarios abandona una app si pierde su progreso al cerrarla? Nadie quiere volver a escribir su nombre, su ciudad o su carrito de compras cada vez que abre la aplicación. Esa frustración tiene solución, y tú puedes implementarla hoy.

Por qué el almacenamiento local importa tanto

Imagina a Carlos, desarrollador freelance en Monterrey. Creó una app de registro de gastos para pequeñas empresas. El problema: cada vez que el usuario cerraba la app, todos los datos desaparecían. Sus clientes se quejaban constantemente.

Carlos no necesitaba una base de datos en la nube. Solo necesitaba guardar información en el propio teléfono. Cuando aprendió a usar SharedPreferences y SQLite, sus usuarios dejaron de quejarse. Las reseñas en Google Play subieron de 2.8 a 4.5 estrellas en un mes.

Existen dos herramientas principales para almacenamiento local en Flutter: SharedPreferences para datos simples y SQLite para datos más complejos. Aprenderás ambas en esta lección.

SharedPreferences: para datos pequeños y rápidos

SharedPreferences es perfecta para guardar configuraciones, preferencias del usuario o pequeños valores como el nombre, el tema oscuro activado o el último precio consultado.

Piensa en ella como una libreta de notas. Guardas clave-valor: "nombre": "Ana", "tema_oscuro": true, "ultimo_monto": 350. Es rápida, sencilla y no requiere estructura compleja.

Cómo instalarla

Agrega el paquete en tu archivo pubspec.yaml:

dependencies:
  shared_preferences: ^2.2.2

Luego corre flutter pub get en tu terminal.

Cómo guardar y leer datos

Aquí un ejemplo real. Supón que tu app de cupones de Liverpool guarda el nombre del usuario para personalizar el saludo:

import 'package:shared_preferences/shared_preferences.dart';

// Guardar el nombre del usuario
Future<void> guardarNombre(String nombre) async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString('nombre_usuario', nombre);
}

// Leer el nombre del usuario
Future<String> leerNombre() async {
  final prefs = await SharedPreferences.getInstance();
  return prefs.getString('nombre_usuario') ?? 'Invitado';
}

Nota el operador ??. Si no existe el dato guardado, regresa 'Invitado' por defecto. Ese detalle evita errores cuando el usuario abre la app por primera vez.

El caso de Fernanda y su app de ahorro

Fernanda trabaja en el área de finanzas personales de una startup en CDMX. Desarrolló una app que ayuda a empleados de FEMSA a registrar sus metas de ahorro mensuales.

Usó SharedPreferences para guardar la meta del mes: "meta_ahorro": 3500. Cada vez que el usuario abría la app, veía su meta sin escribirla de nuevo. Simple, pero efectivo.

El resultado: el tiempo promedio dentro de la app subió de 1 minuto a 4 minutos por sesión. Los usuarios sentían que la app los "recordaba".

SQLite: para datos estructurados y más complejos

Cuando necesitas guardar listas, tablas o relaciones entre datos, SharedPreferences ya no alcanza. Ahí entra SQLite.

SQLite es una base de datos completa que vive dentro del teléfono. No necesita servidor ni conexión. Es la misma tecnología que usan apps como WhatsApp para guardar mensajes offline.

Cómo instalarla

En Flutter usamos el paquete sqflite:

dependencies:
  sqflite: ^2.3.0
  path: ^1.9.0

Estructura básica con un ejemplo real

Supón que construyes una app para repartidores de Bimbo. Necesitas guardar cada entrega: cliente, producto y monto. Aquí está la estructura:

import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

Future<Database> abrirBaseDatos() async {
  final ruta = await getDatabasesPath();
  return openDatabase(
    join(ruta, 'entregas_bimbo.db'),
    onCreate: (db, version) {
      return db.execute(
        'CREATE TABLE entregas('
        'id INTEGER PRIMARY KEY AUTOINCREMENT,'
        'cliente TEXT,'
        'producto TEXT,'
        'monto REAL)'
      );
    },
    version: 1,
  );
}

Esta función crea la base de datos la primera vez. Si ya existe, simplemente la abre. El parámetro version: 1 sirve para manejar migraciones futuras.

Insertar y consultar registros

// Insertar una entrega
Future<void> registrarEntrega(String cliente, String producto, double monto) async {
  final db = await abrirBaseDatos();
  await db.insert('entregas', {
    'cliente': cliente,
    'producto': producto,
    'monto': monto,
  });
}

// Consultar todas las entregas
Future<List<Map<String, dynamic>>> obtenerEntregas() async {
  final db = await abrirBaseDatos();
  return await db.query('entregas');
}

Ahora en tu pantalla puedes mostrar la lista completa de entregas del día, incluso sin señal de celular. Perfectamente útil para zonas industriales o colonias con mala conectividad.

Mostrar los datos en pantalla

FutureBuilder<List<Map<String, dynamic>>>(
  future: obtenerEntregas(),
  builder: (context, snapshot) {
    if (!snapshot.hasData) return CircularProgressIndicator();
    final entregas = snapshot.data!;
    return ListView.builder(
      itemCount: entregas.length,
      itemBuilder: (context, index) {
        final e = entregas[index];
        return ListTile(
          title: Text(e['cliente']),
          subtitle: Text(e['producto']),
          trailing: Text('\$${e['monto'].toStringAsFixed(0)}'),
        );
      },
    );
  },
)

Usa FutureBuilder porque leer de la base de datos es una operación asíncrona. Mientras carga, muestra un indicador. Cuando termina, renderiza la lista.

¿Cuándo usar cada opción?

Situación Herramienta recomendada
Guardar nombre, tema, token de sesión SharedPreferences
Guardar lista de productos o pedidos SQLite
Guardar un número o booleano SharedPreferences
Guardar historial de transacciones SQLite
Configuraciones simples de la app SharedPreferences
Datos con relaciones entre tablas SQLite

La regla práctica: si puedes describir el dato en una sola línea, usa SharedPreferences. Si necesitas una tabla, usa SQLite.

Errores comunes que debes evitar

Muchos desarrolladores cometen estos errores cuando empiezan. Tú ya no los cometerás.

Error 1: No usar async/await correctamente. Tanto SharedPreferences como SQLite son operaciones asíncronas. Si no usas await, leerás null aunque el dato exista. Siempre espera la respuesta antes de usarla.

Error 2: Guardar datos sensibles sin encriptar. SharedPreferences guarda en texto plano. Nunca guardes contraseñas ni tokens permanentes ahí. Para datos sensibles, usa el paquete flutter_secure_storage.

Error 3: Abrir la base de datos múltiples veces. No llames abrirBaseDatos() en cada función de forma independiente sin control. Usa el patrón Singleton para tener una sola instancia activa. Esto evita conflictos y mejora el rendimiento.

Error 4: Olvidar el valor por defecto. Cuando lees con getString o getInt, el resultado puede ser null. Siempre usa ?? valor_default para evitar crashes inesperados en producción.

Lo que aprendió Miguel en Guadalajara

Miguel desarrolla apps para restaurantes en Guadalajara. Su cliente principal tiene tres sucursales y necesitaba que los meseros registraran pedidos aunque el WiFi fallara.

Miguel implementó SQLite para guardar cada pedido localmente. Cuando regresaba la conexión, sincronizaba con el servidor. Esta técnica se llama "offline-first" y es muy valorada en el mercado mexicano, donde la conectividad no siempre es estable.

El restaurante aumentó su eficiencia en horas pico un 30%. Miguel cobró $28,000 por ese proyecto. Saber almacenamiento local le abrió una especialidad rentable.

Puntos clave para recordar

Antes de pasar a la siguiente lección, asegúrate de tener claros estos puntos:

  • Elige SharedPreferences para valores simples y configuraciones rápidas.
  • Elige SQLite cuando necesites listas, tablas o datos estructurados.
  • Siempre usa async/await al leer o escribir datos locales.
  • Nunca guardes contraseñas en SharedPreferences sin encriptar.
  • El patrón "offline-first" es una ventaja competitiva real en el mercado mexicano.

Puntos clave

  • `SharedPreferences` es ideal para guardar datos simples como nombre de usuario, configuraciones o preferencias. Es rápida y fácil de implementar con pares clave-valor.
  • `SQLite` con el paquete `sqflite` te permite guardar listas y tablas completas dentro del dispositivo, sin necesitar internet ni servidor externo.
  • Siempre usa `async/await` al interactuar con almacenamiento local. Son operaciones asíncronas y leer sin `await` puede devolver `null` aunque el dato exista.
  • Nunca guardes contraseñas ni tokens permanentes en `SharedPreferences`, ya que los datos se guardan en texto plano. Usa `flutter_secure_storage` para información sensible.
  • El enfoque 'offline-first' — guardar localmente y sincronizar después — es muy valioso en México, donde la conectividad puede ser inconsistente en zonas industriales o rurales.

Comparte esta lección: