Cómo Guardar Objetos en SharedPreferences en Flutter

- Andrés Cruz

EN In english

En el desarrollo de aplicaciones en Flutter, muchas veces es necesario almacenar valores de manera persistente en la aplicación; para esto, tenemos muchas formas, la más completa sería emplear una base de datos en SQLite, pero, en caso de que solamente queramos almacenar unos pocos datos, como el usuario y datos referentes como un carrito de compra, cursos comprados, etc, podemos optar por un enfoque más sencillo como lo es las preferencias de usuarios, que es un paquete disponibles para Android, iOS, Windows, MacOS y cualquier tecnología que quieras emplear.

Partimos en la cual, ya tenemos datos que persistir porque aprendimos a enviar y recibir datos de Internet mediante una aplicación en Flutter.

⭐ Qué es SharedPreferences y para qué sirve en Flutter

SharedPreferences es un sistema de almacenamiento local persistente, pensado para guardar datos simples en forma de pares clave-valor, como cadenas, números, booleanos o listas.

A diferencia de las variables en memoria, estos valores se guardan en disco, así que permanecen disponibles incluso si el usuario cierra la app.

SharedPreferences es un mecanismo de almacenamiento persistente que permite a las aplicaciones Flutter (tanto en Android como en iOS) guardar datos en forma de pares clave-valor; al ser persistentes, significa que no están solamente en memoria, sino, esta en disco, almacenados en una memoria no volátil y pueden ser fácilmente consultados y escritos.

Los datos son almacenados en XML en todas las aplicaciones; son ideales para gestionar datos de usuario como el ID, email, si está autenticado y otros datos relacionados, su implementación y uso es muy sencillo.

Almacenamiento clave-valor explicado de forma sencilla

En Android se almacena en XML; en iOS en UserDefaults; y Flutter abstrae todo para que tú solo trabajes con una misma API.

Es ideal para:

  • Estado de sesión del usuario
  • Preferencias de configuración
  • Flags como “el usuario ya vio el onboarding”
  • Datos pequeños que no requieren base de datos

Cuándo elegir SharedPreferences en lugar de SQLite u otras opciones

Úsalo cuando:

  • Los datos son simples
  • No hay relaciones ni estructuras complejas
  • Requieres velocidad sin levantar una BD

No lo uses cuando:

  • Necesitas almacenar listas grandes u objetos complejos
  • Se requiere cifrado o seguridad avanzada
  • Tu app maneja grandes volúmenes de datos

※ En mi app de academia, por ejemplo, guardo datos simples como el ID del usuario y si mantiene la sesión iniciada. Para cargar cursos o progreso, ahí sí uso mecanismos más robustos.

⭐ Instalación del paquete SharedPreferences en Flutter

Agrega el paquete en tu pubspec.yaml:

dependencies:
 shared_preferences: ^2.2.0

Luego ejecuta:

$ flutter pub get

Configuración

No necesitas configuración adicional. Como todo sistema de lectura/escritura en disco, las operaciones son asíncronas, así que usarás async y await.

Consideraciones según plataforma

  • Android: usa XML.
  • iOS/MacOS: se integra con UserDefaults.
  • Web: usa LocalStorage, con ciertas limitaciones.
  • Windows/Linux: almacenamiento local vía FFI.

⭐ Cómo guardar y leer datos básicos (String, int, bool, double, listas)

Como indicamos antes, con los SharedPreferences podemos escribir y leer (además de leer), así que, tenemos métodos para tal fin:

  • getKeys(): Retorna todas las claves almacenadas.
  • get(String key): Retorna el valor asociado con la clave especificada.
  • getBool(String key): Retorna un valor booleano asociado con la clave.
  • getInt(String key): Retorna un valor entero asociado con la clave.
  • getDouble(String key): Retorna un valor decimal (double) asociado con la clave.
  • getString(String key): Retorna un valor de texto (string) asociado con la clave.
  • getStringList(String key): Retorna una lista de valores de texto asociada con la clave.

Todos los métodos son lo mismo, pero, dependiendo del tipo de dato que quieras almacenar, tenemos un método distinto, como números, string, entre otros; en cuando los métodos para escribir:

  • setBool(String key, bool value): Guarda un valor booleano asociado con la clave.
  • setInt(String key, int value): Guarda un valor entero asociado con la clave.
  • setDouble(String key, double value): Guarda un valor decimal asociado con la clave.
  • setString(String key, String value): Guarda un valor de texto asociado con la clave.
  • setStringList(String key, List<String> value): Guarda una lista de valores de texto asociada con la clave.

Eliminación:

  • remove(String key): Elimina el par clave-valor asociado con la clave especificada.

También, pasar un valor nulo en cualquiera de los setters equivale a llamar a remove(key).

Ejemplo Práctico

Supongamos que tenemos una clase Person. Veamos cómo guardar una instancia de esta clase en SharedPreferences:

import 'package:shared_preferences/shared_preferences.dart';
class Person {
 final String name;
 final int age;
 Person(this.name, this.age);
}
void savePersonToSharedPreferences(Person person) async {
 final prefs = await SharedPreferences.getInstance();
 prefs.setString('person_name', person.name);
 prefs.setInt('person_age', person.age);
}
Future<Person?> getPersonFromSharedPreferences() async {
 final prefs = await SharedPreferences.getInstance();
 final name = prefs.getString('person_name');
 final age = prefs.getInt('person_age');
 if (name != null && age != null) {
   return Person(name, age);
 }
 return null;
}

El siguiente paso es realizar la implementación anterior, en el cual, podemos registrar y obtener datos de usuario; antes de usarlo en cualquier parte de la aplicación, debemos de crear la instancia:

final prefs = await SharedPreferences.getInstance();

Como todas operaciones, son asíncronas, lo cual es normal en este tipo de tecnologías en donde se obtienen datos del disco.

Ahora, con este objeto, podemos establecer datos con los métodos mencionados antes, por ejemplo, un string:

prefs.setString(key, value);

Si es un objeto, puedes almacenar los datos en formato JSON:

prefs.setString(key, json.encode(value));

Aquí tienes un ejemplo completo:

save(String key, value) async {
 final prefs = await SharedPreferences.getInstance();
 prefs.setString(key, json.encode(value));
}

Y finalmente permite leer el objeto u otro:

get(String key) async {
 final prefs = await SharedPreferences.getInstance();
 return json.decode(prefs.getString(key));
}

Ejemplos claros de get/set

prefs.setString('nombre', 'Carlos');
prefs.setInt('edad', 28);
prefs.setBool('logueado', true);

final nombre = prefs.getString('nombre');
final edad = prefs.getInt('edad');
final logueado = prefs.getBool('logueado');

Uso en operaciones asíncronas: por qué todo es async

La instancia se obtiene desde disco o almacenamiento del sistema operativo. Flutter lo maneja como operación costosa y por eso requiere await.

Errores comunes

  • Recuperar datos antes de obtener la instancia.
  • Asumir que el valor existe (si no existe, devuelve null).
  • Guardar valores demasiado grandes.

⭐ Guardar datos de usuario: ejemplo práctico basado en mi aplicación

En mi caso, para mi app de academia necesitaba que el usuario permaneciera conectado aunque cerrara la app, así que guardaba un par de claves esenciales en SharedPreferences:

  • user_id
  • is_logged_in
  • user_email

Guardar la sesión del usuario

Future<void> saveSession(String id, String email) async {
 final prefs = await SharedPreferences.getInstance();
 await prefs.setString('user_id', id);
 await prefs.setString('user_email', email);
 await prefs.setBool('is_logged_in', true);
}

Recuperar los datos al iniciar la app

Future<bool> isLoggedIn() async {
 final prefs = await SharedPreferences.getInstance();
 return prefs.getBool('is_logged_in') ?? false;
}

Limpieza de datos al cerrar sesión

Future<void> logout() async {
 final prefs = await SharedPreferences.getInstance();
 await prefs.remove('user_id');
 await prefs.remove('user_email');
 await prefs.setBool('is_logged_in', false);
}

En la práctica, esto me permitió que los alumnos volvieran a la app y siguieran conectados sin requerir reautenticación constante; uso el sistema de JSON WEB TOKEN para también guardar el token de autenticación del usuario.

Buenas prácticas y Limitaciones de SharedPreferences y cuándo NO deberías usarlo

  • No guardes listas grandes ni objetos enormes.
  • Solo usa JSON para estructuras pequeñas.
  • Si te encuentras guardando demasiadas cosas, probablemente necesitas Hive o SQLite.
  • contraseñas
  • llaves privadas

⭐ Preguntas frecuentes

  • Es seguro usar SharedPreferences para contraseñas?
    • No. Usa secure storage.
  • ¿Puedo usar SharedPreferences en Web?
  • Sí, aunque internamente utiliza localStorage y tiene limitaciones.
    • ¿Puedo guardar listas u objetos?
  • Listas de strings sí; objetos → solo usando JSON.
  • ¿Cómo evito errores de tipo?
    • Validando null y manejando conversiones JSON de forma segura.

Conclusión

SharedPreferences es una herramienta ligera, rápida y perfecta cuando solo necesitas guardar datos simples. En mi experiencia creando una app de academia, fue clave para manejar la sesión del usuario y varias preferencias sin tener que levantar bases de datos o sistemas más complejos.

Usado con criterio, es una de las formas más efectivas de implementar persistencia local en Flutter.

Los SharedPreferences son perfectos para guardar datos simples, pero, si quieres emplear una base de datos real, tenemos SQLite/SQFlite de Flutter. 

En este artículo introduciremos este tema para almacenar datos de manera persistentes mediante SharedPreferences en Flutter, opciones, recomendaciones y ejemplos.

Acepto recibir anuncios de interes sobre este Blog.

Andrés Cruz

EN In english