Flutter: ¿Cómo salvar datos en el almacenamiento local?

Una de las funcionalidades que muchos programadores utilizan cuando desarrollan una aplicación móvil es guardar datos en el almacenamiento local (localstorage), esto puede ser útil para guardar configuraciones del sistema, almacenar tokens, credenciales o otros datos. En este artículo les vamos a mostrar como hacer esto en Flutter.

Importando la dependencia

Una de las formas para salvar datos en el almacenamiento es a partir del paquete “shared_preferences“, pero requiere que lo instalemos de internet, para hacer esto, primeramente buscamos la versión más reciente en el sitio de dependencias de fluter (pub.dev) (en el momento de este artículo se trabajó con la versión más reciente 0.5.7+3) y en la pestaña “Installing” nos informa la versión a utilizar. Lo copiamos y lo pegamos en el archivo de configuración del proyecto pubspec.yaml en la sección de las dependencias de producción.

dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^0.5.7+3

Deben fijarse que se adiciona a un nivel con respecto a “dependencies” como exige el formato de archivo yaml.

Una vez guardado el archivo, pueden descargar la dependencia presionando el enlace “Pug get” desde el mismo editor de Android Studio (este y otros enlaces relacionados con el manejo de dependencias están justamente en una barra arriba cuando abren el archivo de configuración pubspec.yaml alineados a la derecha), lo pueden hacer desde la terminal y estando en la ruta del proyecto con el comando “flutter pub get” o simplemente correr el proyecto y si no está la dependencia, como primera acción la descargará.

Escribiendo el código

Antes de mostrarles los widgets principales de nuestra aplicación, quiero darles un truco para utilizar las funciones de almacenamiento de este paquete de una manera más cómoda y que nos permita reutilizarlos con la menor cantidad de código posible. Primeramente creemos en la raíz de la carpeta lib/ una carpeta (paquete) llamado preference y dentro dos archivos preferences_utils.dart y prefs.dart.

Veámos el archivo preferences_utils.dart:

import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';
class PreferencesUtils {
  static Future<SharedPreferences> get prefs => SharedPreferences.getInstance();
  static Future<bool> getBool(String key) async {
    final p = await prefs;
    return p.getBool(key) ?? false;
  }
  static Future setBool(String key, bool value) async {
    final p = await prefs;
    return p.setBool(key, value);
  }
}

Expliquemos:

Línea 1 y 2: Importamos la función async ya que para obtener los datos del almacenamiento es necesario esperar un tiempo y las funciones del paquete shared_preferences.
Línea 6: Creamos una instancia de SharedPreferences, a través de su instancia vamos a obtener y escribir los datos en el almacenamiento local.
Línea 8-11: Función genérica para obtener el valor a partir de la llave, en este caso el dato que se maneja es un booleano. Si no existe la llave o el valor devuelve por defecto false.
Línea 13-16: Función genérica para salvar un dato a partir de la llave, en este caso el dato que se maneja es un booleano.

Veámos el archivo prefs.dart:

import 'package:localstorage/preference/preferences_utils.dart';
class Storage {
  // Gets
  static Future<bool> get data => PreferencesUtils.getBool("data");
  // Sets
  static void setData(v) => PreferencesUtils.setBool("data", v);
}

Expliquemos:

Línea 4 y 6: Uso de la funciones getBool y setBool de la clase PreferencesUtils asociado a una llave específica.

Esto es una forma de implementar funciones reutilizables para el almacenamiento local a través de la librería shared_preferences, aunque esto lo puedes hacer a tu forma. Ahora vamos a seguir con nuestra aplicación aplicando esto.

Veámos el archivo main.dart (similar al artículo anterior), este inicia la aplicación con el widget HomePage() del archivo home.dart:

import 'package:flutter/material.dart';
import 'package:localstorage/ui/home.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Almacenamiento local',
      theme: ThemeData(
        primarySwatch: Colors.pink,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: HomePage(),
    );
  }
}

Veámos el archivo home.dart:

import 'package:flutter/material.dart';
import 'package:localstorage/preference/prefs.dart';
class HomePage extends StatefulWidget {
  HomePage({Key key}) : super(key: key);
  @override
  _HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
  bool data = false;
  @override
  void initState() {
    _load();
    super.initState();
  }
  _load() {
    Storage.data.then((v) => {
      setState(() {
        data = v ;
      })
    });
  }
  _save(v) {
    setState(() {
      data = v;
    });
    Storage.setData(v);
  }
  @override
  Widget build(BuildContext context) {
    return Material(
      child: Scaffold(
        appBar: new AppBar(
          title: new Text(
            "Home",
            style: TextStyle(color: Colors.white),
          ),
        ),
        body: Container(
            child: Center(
                child: Card(
          color: Colors.pink,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(10.0),
          ),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              ListTile(
                  leading: Icon(Icons.save, size: 50, color: Colors.white),
                  title: Text("Dato", style: TextStyle(color: Colors.white)),
                  subtitle: Text(
                      "salvado como " + ((data) ? "verdadero" : "falso "),
                      style: TextStyle(
                          color: Colors.white, fontStyle: FontStyle.italic)),
                  trailing:
                      Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
                    Switch(
                        activeColor: Colors.white,
                        inactiveTrackColor: Colors.black45,
                        value: data,
                        onChanged: _save)
                  ]))
            ],
          ),
        ))),
      ),
    );
  }
}

Expliquemos el código:

Línea 4-9: Creación del widget de mantenimiento de estado HomePage e inicialización de la clase de estado _HomePageState
Línea 12: Inicialización de la variable que será manejada por el estado con valor por defecto false.
Línea 14-18: Inicialización del estado, llamada de la función _load() el cual obtiene el valor de data del almacenamiento local.

Nota: En Dart, una función empiece con _ es privada.

Línea 20-26: Función que carga el valor de data (aquí se llama Storage.data).
Línea 28-33: Función que actualiza el valor de data y además lo guarda en el almacenamiento local.
Línea 36-75: Construye el widget de la interfaz de usuario con un Switch para cambiar el valor a data.

Hasta aquí una de las formas de guardar datos en el almacenamiento local, puedes ver el código en el siguiente enlace: github.