Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 34 di 37
  • livello intermedio
Indice lezioni

Shared Preferences

Flutter permette diversi approcci per salvare i dati nel contesto dello sviluppo di un'app mobile. Uno di questi è basato sull'uso delle SharedPreferences.
Flutter permette diversi approcci per salvare i dati nel contesto dello sviluppo di un'app mobile. Uno di questi è basato sull'uso delle SharedPreferences.
Link copiato negli appunti

Nella precedente lezione abbiamo preso familiarità con il plugin path_provider e la classe File per effettuare operazioni di lettura e scrittura su un file di testo salvato nella document folder gestita dalla applicazione.

Questo ovviamente non è l’unico modo per salvare le informazioni dell’utente che utilizza la nostra applicazione. Un altro approccio definito in Flutter per il salvataggio dei dati è offerto dalle SharedPreferences.

Le shared preferences permettono di salvare delle coppie chiave-valore (key-value, KV) costituite da valori primitivi come stringhe, interi, valori in virgola mobile o booleani, che rappresentano ad esempio delle impostazioni o preferenze inserite dall’utente. I dati archiviati utilizzando le shared preferences sono mantenuti privati ​​nell'ambito dell'app rendendo le shared preferences adatte in diversi scenari come:

  • salvataggio delle impostazioni dell'utente per la nostra app;
  • archiviare dati che possono essere utilizzati in diverse attività all'interno dell'app.

In questa lezione capiremo come utilizzare le shared preferences per salvare le impostazioni dell’utente.

Il plugin shared_preferences e la classe SharedPreferences

In Flutter, per effettuare la lettura e la scrittura di file è necessario utilizzare:

Plugin/Classe Descrizione
shared_preferences questo plugin permette allo sviluppatore di:

  • utilizzare le classi NSUserDefaults per iOS e SharedPreferences per Android;
  • salvare in modo persistente e asincrono i dati sul disco per salvare informazioni basate su tipi primitivi
SharedPreferences È la classe che permette la scrittura e la lettura asincrona delle coppie KV e offre un insieme di metodi per:

  • salvare una coppia KV;
  • leggere il valore di una chiave;
  • rimuovere una coppia KV;
  • verificare se esiste una chiave.

Esempio pratico

Come sempre, creiamo un nuovo progetto come descritto nella lezione 6.

Procediamo adesso step by step per creare un’applicazione che ci permetta di aggiungere e rimuovere coppie KV dalle nostre shared preferences immaginando di creare una semplice pagina di settings.

Installazione del plugin shared_preferences

Installiamo il plugin shared_preferences.

Apriamo il file pubspec.yaml e inseriamo sotto la voce dependencies il nome del plugin come segue

dependencies:
  shared_preferences: ^0.5.6+3

Eseguiamo dal nostro terminale il comando

flutter pub get

per installare il plugin.

Definizione della struttura dell’applicazione

Come mostrato nella lezione 32, definiamo la corretta struttura del nostro progetto e creiamo la seguente struttura di cartelle e file .dart, come mostrato di seguito.

lib/
│── screens/
│   │── screen1
│   │      │── components
│   │      │      │── body.dart
│   │      │── screen1.dart
│── theme/
│   │── style.dart
│── services/
│   │── utilities.dart
│── routes.dart

Definizione dell’UI

Ai fini di questo esempio, concentriamo l’attenzione solo ed esclusivamente sulla creazione della nostra interfaccia, lasciando l’implementazione dei temi, delle rotte e del refactoring del file main.dart al lettore. Una possibile implementazione di questi file sarà comunque disponibile su GitHub.

La schermata screen1 sarà costituita da:

  • un TextField per scrivere una email;
  • uno SwitchListTile per abilitare/disabilitare le notifiche;
  • un TextField in cui riportare il numero di elementi da mostrare in una lista;
  • un RaisedButton per reimpostare i settings di default.

Definiamo quindi la struttura principale della schermata in screen1.dart

class Screen1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Screen1'),
      ),
      body: Body(),
    );
  }
}

Nel file body.dart invece definiamo il contenuto della nostra pagina. Vediamo una possibile implementazione di seguito.

class Body extends StatefulWidget {
  Body({Key key}) : super(key: key);
  @override
  _BodyState createState() => _BodyState();
}
class _BodyState extends State<Body> {
  String _email = "";
  bool _isNotification = false;
  int _totalItems = 0;
  final myEmailController = TextEditingController();
  final myTotalItemController = TextEditingController();
  @override
  void initState() {
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Column(children: <Widget>[
      TextField(
          decoration: InputDecoration(
            hintText: 'write your email',
            hintStyle: TextStyle(color: Colors.grey),
            icon: Icon(Icons.email),
          ),
          maxLines: 1,
          maxLength: 100,
          controller: myEmailController,
    ),
      Divider(),
      SwitchListTile(
        value: _isNotification,
        title: Text("Notification"),
        onChanged: (value) {  });
        },
        secondary: Icon(Icons.notifications),
      ),
      Divider(),
      TextField(
          decoration: InputDecoration(
            hintText: '0',
            hintStyle: TextStyle(color: Colors.grey),
            icon: Icon(Icons.list),
          ),
          keyboardType: TextInputType.number,
          inputFormatters: <TextInputFormatter>[
            WhitelistingTextInputFormatter.digitsOnly
          ],
          maxLines: 1,
          maxLength: 3,
          controller: myTotalItemController,
      Divider(),
      RaisedButton(
        onPressed: () {  },
        child: Text('Reset Settings')
      )
    ]);
  }
}

Come di consueto, non abbiamo ancora implementato i metodi necessari per gestire al meglio la nostra interfaccia per:

  • aggiornare lo stato con i valori correnti;
  • caricare dalle SharedPreferences gli eventuali valori disponibili;
  • inserire i valori nelle SharedPreferences;
  • resettare i valori dei settings a quelli di default.

Ora che la nostra interfaccia è pronta passiamo allo sviluppo del servizio di gestione delle shared preferences.

Definizione della logica per la gestione delle shared preferences

Creiamo all’interno della cartella services il file utilities.dart in cui definire la classe statica SharedPreferencesManager, che sarà responsabile:

  • della definizione delle variabili statiche e finali che rappresentano le chiavi dei valori che vogliamo salvare;
  • del recupero dell’istanza di SharedPreferences;
  • del salvataggio dei dati in base al tipo;
  • di rimuovere dalle SharedPreferences una lista di chiavi.

Compresi i requisiti, vediamo una semplice implementazione.

class SharedPreferencesManager {
  static final String emailKey = "email";
  static final String notificationKey = "alert";
  static final String totalItems = "totalItems";
  static Future<SharedPreferences> getSharedPreferencesInstance() async {
    return await SharedPreferences.getInstance();
  }
  static void saveKV(String key, dynamic value) async {
    SharedPreferences sharedPreferences = await getSharedPreferencesInstance();
    if (value is bool) {
      sharedPreferences.setBool(key, value);
    } else if (value is String) {
      sharedPreferences.setString(key, value);
    } else if (value is int) {
      sharedPreferences.setInt(key, value);
    } else if (value is double) {
      sharedPreferences.setDouble(key, value);
    } else if (value is List<String>) {
      sharedPreferences.setStringList(key, value);
    }
  }
  static void resetSharedPreferences(List<String> list) async{
    SharedPreferences sharedPreferences = await getSharedPreferencesInstance();
    for(String key in list)
      sharedPreferences.remove(key);
  }
}

In questo scenario, abbiamo definito i metodi getSharedPreferencesInstance() e saveKV() affinché fossero statici e asincroni per permettere il recupero dell’istanza di SharedPreferences. Inoltre, abbiamo centralizzato nel metodo saveKV() la scrittura delle coppie KV in base al tipo di valore passato in input. Ad esempio, nel caso di un valore di tipo bool verrà utilizzato il metodo sharedPreferences.setBool().

Infine, nel metodo resetSharedPreferences() abbiamo offerto la possibilità di cancellare dalle SharedPreferences una lista di coppie KV a partire da un array di stringhe rappresentanti le chiavi che si vogliono rimuovere. Per farlo abbiamo usato il metodo sharedPreferences.remove().

L’unico aspetto non gestito da questa classe è l’estrazione dei dati dalle SharedPreferences in base alla chiave passata come parametro. Questo aspetto è troppo specifico per essere generalizzato.

Aggiornamento della UI con la logica dei servizi

Creiamo i tre metodi che ci permetteranno di:

  • caricare i settings dalle SharedPrefences;
  • salvare i dati inseriti dall’utente nei TextField quando l’evento onChanged viene invocato dal framework di Flutter.

Definiamo il metodo _loadSettings() che recupera un’istanza di SharedPreferences tramite il metodo SharedPreferencesManager.getSharedPreferencesInstance() e carica i possibili valori esistenti in SharedPreferences assegnandoli ai TextController dei TextView e alla variabile bool _isNotification.

_loadSettings() async {
    SharedPreferences sharedPrefs =
        await SharedPreferencesManager.getSharedPreferencesInstance();
    setState(() {
      myEmailController.text = (sharedPrefs.getString(SharedPreferencesManager.emailKey) ?? "");
      _isNotification = (sharedPrefs.getBool(SharedPreferencesManager.notificationKey) ??false);
      myTotalItemController.text = (sharedPrefs.getInt(SharedPreferencesManager.totalItems) ?? 0).toString();
    });
  }

Come possiamo notare, tramite SharedPreferences abbiamo invocato i metodi getString, getInt e getBool per estrapolare le informazioni di interesse, se salvate.

Aggiorniamo quindi il metodo initState e la proprietà onPressed del RaisedButton.

void initState() {
    super.initState();
    _loadSettings();
  }
// . . .
    RaisedButton(
        onPressed: () {
          SharedPreferencesManager.resetSharedPreferences([
            SharedPreferencesManager.emailKey,
            SharedPreferencesManager.notificationKey,
            SharedPreferencesManager.totalItems
          ]);
          setState(() {
            _loadSettings();
          });
        },
        child: Text('Reset Settings')
      )
// . . .

In questo modo:

  • al caricamento della pagina verrà caricato lo stato iniziale per tutti i campi dei nostri settings;
  • abbiamo utilizzato per il RaisedButton il metodo SharedPreferencesManager.resetSharedPreferences() passando la lista di chiavi da rimuovere dalla shared preferences e abbiamo definito l’aggiornamento dello stato.

A questo punto dobbiamo permettere ai TextView di aggiornare le SharedPreferences quando l’utente effettua dei cambiamenti nel testo.

void _onTotalItemChanged(String value) {
    SharedPreferencesManager.saveKV(SharedPreferencesManager.totalItems, int.parse(value));
  }
  void _onEmailChanged(String value) {
    SharedPreferencesManager.saveKV(SharedPreferencesManager.emailKey, value);
  }

Aggiorniamo quindi la proprietà onChanged dei TextView come segue.

TextField(
      // . . .
      onChanged: _onEmailChanged),
 // . . .
 TextField(
      // . . .
      onChanged: _onTotalItemChanged),

Aggiorniamo infine il metodo onChanged dello SwitchListTile che si occuperà di aggiornare lo stato e di effettuare il salvataggio della coppia KV per le notifiche.

SwitchListTile(
// . . .
onChanged: (value) {
  setState(() {
    _isNotification = value;
    SharedPreferencesManager.saveKV(SharedPreferencesManager.notificationKey, value);
  });
}
),

Eseguiamo l’applicazione per vedere il risultato delle modifiche.

Figura 177a. Esempio di utilizzo delle shared preferences per Android (click per ingrandire)Esempio di utilizzo delle shared preferences per Android
Figura 177b. Esempio di utilizzo delle shared preferences per iOS (click per ingrandire)Esempio di utilizzo delle shared preferences per iOS

Il codice di questa lezione è disponibile su GitHub.

Ti consigliamo anche