flutter-shared-preferences-persistencia

Guardar preferencias en Flutter con shared_preferences

  • 3 min

La persistencia básica es guardar pequeños datos para que sigan ahí cuando la app se cierre.

Imagina que tu usuario activa el “Modo Oscuro”. Cierra la App, la abre mañana, y… ¡Zas! Vuelve a estar en blanco cegador. Eso es una mala experiencia.

Para guardar datos pequeños y simples (preferencias o configuraciones) no necesitamos una base de datos compleja. Podemos usar un sistema nativo de pares clave-valor mediante el paquete shared_preferences.

¿Qué es y qué NO es?

  • SÍ ES: Un lugar para guardar “cosas sueltas”: un boolean (visto/no visto), un int (puntuación alta), un string (nombre de usuario).
  • NO ES: Una base de datos. No intentes guardar ahí una lista de 5.000 productos. Es lento para grandes volúmenes y no permite búsquedas complejas.
  • NO ES: Un almacén seguro. No guardes contraseñas, tokens ni otros secretos sin usar una solución de almacenamiento seguro para cada plataforma.

Instalación

Añádelo a tu pubspec.yaml:

flutter pub add shared_preferences
Copied!

Guardar datos

Usar shared_preferences es asíncrono (tarda unos milisegundos porque escribe en el almacenamiento del dispositivo).

import 'package:shared_preferences/shared_preferences.dart';

Future<void> guardarPreferencias() async {
  final prefs = SharedPreferencesAsync();

  // Guardamos valores usando una clave
  await prefs.setBool('modo_oscuro', true);
  await prefs.setString('username', 'LuisLlamas');
  await prefs.setInt('puntuacion_maxima', 1500);
  
  print("Datos guardados");
}
Copied!

Las claves ('modo_oscuro', 'username') son sensibles a mayúsculas. Una buena práctica es crear una clase con constantes estáticas (static const String keyUser = 'user') para no equivocarse al escribir el string.

Recuperar datos

Leer es igual de fácil. La única diferencia es que si la clave no existe (es la primera vez que abres la App), te devolverá null.

Future<void> cargarPreferencias() async {
  final prefs = SharedPreferencesAsync();

  // Leemos. El segundo parámetro '?? false' es importante:
  // "Si la clave no existe, devuelve false por defecto".
  bool esOscuro = await prefs.getBool('modo_oscuro') ?? false;
  
  String nombre = await prefs.getString('username') ?? 'Invitado';
  
  print("Hola $nombre, modo oscuro: $esOscuro");
}
Copied!

Borrar datos

Si el usuario restablece una opción, puedes borrar esa preferencia o limpiar todas las claves de la aplicación.

final prefs = SharedPreferencesAsync();

// Borrar una clave específica
await prefs.remove('username');

// Borrar TODO (Resetear la App)
await prefs.clear();
Copied!

Dónde colocar el acceso a preferencias

No crees ni consultes el almacén de preferencias dentro de build. No es el sitio para iniciar trabajo asíncrono y puedes acabar con lecturas repetidas.

Lo correcto es usar el patrón Service (que vimos en el Módulo 6) o inyectarlo en tu Provider (Módulo 7).

La clase SharedPreferences original es una API heredada que el paquete prevé retirar en el futuro. Para código nuevo se recomienda SharedPreferencesAsync o SharedPreferencesWithCache. La primera siempre consulta el almacenamiento de la plataforma; la segunda permite lecturas síncronas desde una caché local.

Ejemplo con un proveedor de tema

Vamos a hacer que el tema oscuro se recuerde.

class ThemeProvider extends ChangeNotifier {
  bool _isDark = false;
  final SharedPreferencesAsync _prefs = SharedPreferencesAsync();
  
  bool get isDark => _isDark;

  // Constructor: Cargamos la preferencia al iniciar
  ThemeProvider() {
    _cargarTema();
  }

  Future<void> _cargarTema() async {
    _isDark = await _prefs.getBool('isDark') ?? false;
    notifyListeners(); // Avisamos a la UI para que se actualice
  }

  Future<void> toggleTheme() async {
    _isDark = !_isDark;
    notifyListeners();

    // Guardamos el cambio
    await _prefs.setBool('isDark', _isDark);
  }
}
Copied!