Flutter - dati in un database locale con SQLite

Se, per la nostra app, abbiamo bisogno di dati persistenti e di eseguire query su grandi quantità di dati sul dispositivo locale, sarebbe meglio utilizzare un database invece di un file locale o di un archivio di valori-chiave. In generale, i database forniscono inserimenti, aggiornamenti e query più rapidi rispetto ad altre soluzioni di persistenza locale.

Le app Flutter possono utilizzare i database SQLite tramite il plug-in sqflite disponibile su pub.dev. In questo esempio mostriamo le basi dell'utilizzo di sqflite per inserire, leggere, aggiornare e rimuovere dati relativi a varie persone.

Questo esempio si compone dei seguenti passaggi:

  1. Aggiungere le dipendenze
  2. Definire il modello dati Persona
  3. Aprire il database
  4. Creare la tabella delle persone
  5. Inserire una persona nel database
  6. Recuperare l'elenco delle persone
  7. Aggiornare una persona nel database
  8. Eliminare una persona dal database

1. Aggiungere le dipendenze

Per utilizzare il database SQLite, importare i pacchetti sqflite e path.

Il pacchetto sqflite fornisce classi e funzioni per interagire con un database SQLite.
Il pacchetto path fornisce funzioni per definire la posizione in cui archiviare il database su disco.

            
dependencies:
  flutter:
    sdk: flutter
  sqflite:
  path:
            
        

Assicurati di importare i pacchetti nel file in cui lavorerai.


import 'dart:async';

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

2. Definire il modello dati Persona

Prima di creare la tabella per memorizzare le informazioni sulle Persone, definiamo i dati che devono essere memorizzati. Per questo esempio, definiamo una classe Persona che contiene tre dati: un id univoco, il nome e l'età di ogni persona.

         
class Persona {
  final int id;
  final String nome;
  final int eta;

  Persona({
    required this.id,
    required this.nome,
    required this.eta,
  });
}    
     

3. Aprire il database

Prima di leggere e scrivere dati nel database, dobbiamo aprire una connessione al database. Ciò comporta due passaggi:

  1. Definiamo il percorso del file del database utilizzando getDatabasesPath() dal pacchetto sqflite, combinato con la funzione join dal pacchetto path.
  2. Apriamo il database con la funzione openDatabase() da sqflite.
  3.          
    // Evita errori causati da upgrade di Flutter.
    // È necessario importare 'package:flutter/widgets.dart'
    
    WidgetsFlutterBinding.ensureInitialized();
    
    // Apri il database e memorizza il riferimento.
    
    final database = openDatabase(
      // Imposta il percorso del database. Nota: utilizzare la funzione ‘join’ dal
      // pacchetto ‘path’ è il modo migliore per assicurarsi che il percorso sia
      // corretto per ogni piattaforma
      join(await getDatabasesPath(), 'database_persone.db'),
    );
             
         

4. Creare la tabella Persone

Creiamo una tabella per memorizzare informazioni sulle varie persone. Per questo esempio, creiamo una tabella denominata persone che definisce i dati che possono essere archiviati. Ogni persona contiene un ID, un nome e un'età. Pertanto, questi sono rappresentati come tre colonne nella tabella delle persone.

  1. L'id è un int Dart e viene memorizzato come un tipo di dati SQLite INTEGER. È inoltre buona norma utilizzare un id come chiave primaria per la tabella per migliorare i tempi di query e aggiornamento.
  2. Il nome è una stringa Dart e viene memorizzato come tipo di dati SQLite TEXT.
  3. Anche l'età è un int Dart e viene memorizzata come tipo di dati INTEGER.
    
final database = openDatabase(
  // Imposta il percorso del database. Nota: utilizzare la funzione ‘join’ dal
  // pacchetto ‘path’ è il modo migliore per assicurarsi che il percorso sia
  // corretto per ogni piattaforma
  join(await getDatabasesPath(), 'database_persone.db'),
  // Quando il database viene creato per la prima volta, crea una tabella per memorizzare le persone.
  onCreate: (db, version) {
    // Esegui il comando CREATE TABLE sul database.
    return db.execute(
      'CREATE TABLE persone(id INTEGER PRIMARY KEY, nome TEXT, eta INTEGER)',
    );
  },
  // Imposta la versione. Questo esegue la funzione onCreate e fornisce un percorso per eseguire upgrade e downgrade del database.
  version: 1,
);
    

5. Inserire una persona nel database

Ora che abbiamo un database con una tabella adatta a memorizzare informazioni sulle varie persone, è il momento di leggere e scrivere dati.

Per prima cosa, inseriamo una persona nella tabella delle persone. Ciò comporta due passaggi:

  1. Converti Persona in Map
  2. Utilizza il metodo insert() per memorizzare la mappa nella tabella persone.
        
class Persona {
  final int id;
  final String nome;
  final int eta;

  Persona({
    required this.id,
    required this.nome,
    required this.eta,
  });

  // Converti una Persona in una Map. Le chiavi devono essere uguali ai nomi delle
  //colonne nel database.
  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'nome': nome,
      'eta': eta,
    };
  }

  // Implementa toString per rendere piu semplice visualizzare informazioni su
  // ogni persona quando viene utilizzato il comando print.
  @override
  String toString() {
    return 'Persona{id: $id, nome: $nome, eta: $eta}';
  }
}
        
    
    
// Definisci una funzione che inserisce le persone nel database
Future<void> insertPersona(Persona persona) async {
  // Ottieni un riferimento al database.
  final db = await database;

  // Inserisci la Persona nella tabella corretta. Puoi anche specificare
  // il `conflictAlgorithm` da utilizzare in caso la persona sia inserita due volte.
  //
  // In questo caso, sostituisci tutti i dati precedenti.
  await db.insert(
    'persone',
    persona.toMap(),
    conflictAlgorithm: ConflictAlgorithm.replace,
  );
}
    
                    
// Crea una Persona e aggiungila alla tabella delle persone
var luca = Persona(
  id: 0,
  nome: 'Luca',
  eta: 30,
);

await insertPersona(luca);
                    
                

6. Recuperare l'elenco delle persone

Ora che una persona è memorizzata nel database, interroghiamo il database per una persona specifica o un elenco di tutte le persone. Ciò comporta due passaggi:

  1. Eseguire una query sulla tabella delle persone. Questo restituisce un List
  2. .
  3. Convertire List in List.
    
// Un metodo che recupera tutte le persone dalla tabella persone.
Future> persone() async {
  // Ottieni un riferimento al database.
  final db = await database;

  // Esegui una query nella tabella per tutte le Persone.
  final List> maps = await db.query('persone');

  // Converti la List in List.
  return List.generate(maps.length, (i) {
    return Persona(
      id: maps[i]['id'],
      nome: maps[i]['nome'],
      eta: maps[i]['eta'],
    );
  });
}
    
                
// Utilizza il metodo indicato sopra per recuperare tutte le persone.
print(await persone());
// Stampa un elenco che include Luca.
                
            

7. Aggiornare una persona nel database

Dopo aver inserito le informazioni nel database, potresti voler aggiornare tali informazioni in un secondo momento. Puoi farlo usando il metodo update() dalla libreria sqflite.

Ciò comporta due passaggi:

  1. Convertire la persona in una mappa.
  2. Usare una clausola where per assicurarsi di aggiornare la persona corretta
    
Future updatePersona(Persona persona) async {
  // Ottieni un riferimento al database.
  final db = await database;

  // Aggiorna la persona.
  await db.update(
    'persone',
    persona.toMap(),
    // Assicurati che la persona abbia un ID corrispondente.
    where: 'id = ?',
    // Passa l’ ID della Persona come whereArg per evitare SQL injection.
    whereArgs: [persona.id],
  );
}
    
                
// Aggiorna l’età di Luca e salvala nel database.
luca = Persona(
  id: luca.id,
  nome: luca.nome,
  eta: luca.eta + 7,
);
await updatePersona(luca);

// Stampa i risultati aggiornati.
print(await persone()); // Stampa Luca con l’eta di 37.
                
            

8. Eliminare una persona dal database

Oltre a inserire e aggiornare le informazioni sulle persone, possiamo anche rimuovere le persone dal database. Per eliminare i dati, utilizzare il metodo delete() dalla libreria sqflite.

In questa sezione, creiamo una funzione che prenda un ID ed elimini la persona con un ID corrispondente dal database. Per fare in modo che funzioni, è necessario fornire una clausola where per limitare l'eliminazione dei record.

    
Future deletePerson(int id) async {
  // Ottieni un riferimento al database.
  final db = await database;

  // Rimuovi la Persona dal database.
  await db.delete(
    'persone',
    // Utilizza una clausola `where` per eliminare dal db una persona specifica
    where: 'id = ?',
    // Passa l’id della Persona come whereArg per prevenire SQL injection.
    whereArgs: [id],
  );
}
    

Esempio Completo

Per eseguire l'esempio:

  1. Crea un nuovo progetto Flutter.
  2. Aggiungi i pacchetti sqflite e path al tuo pubspec.yaml.
  3. Incolla il codice seguente in un nuovo file chiamato lib/db_test.dart.
  4. Esegui il codice con flutter run lib/db_test.dart.
    
import 'dart:async';

import 'package:flutter/widgets.dart';

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

void main() async {
  // Evita errori causati da upgrade di Flutter.
// È necessario importare 'package:flutter/widgets.dart'

WidgetsFlutterBinding.ensureInitialized();

// Apri il database e memorizza il riferimento.
final database = openDatabase(
  // Imposta il percorso del database. Nota: utilizzare la funzione ‘join’ dal
  // pacchetto ‘path’ è il modo migliore per assicurarsi che il percorso sia
  // corretto per ogni piattaforma
  join(await getDatabasesPath(), 'database_persone.db'),
  // Quando il database viene creato per la prima volta, crea una tabella per memorizzare le persone.
  onCreate: (db, version) {
    // Esegui il comando CREATE TABLE sul database.
    return db.execute(
      'CREATE TABLE persone(id INTEGER PRIMARY KEY, nome TEXT, eta INTEGER)',
    );
  },
  // Imposta la versione. Questo esegue la funzione onCreate e fornisce un percorso per eseguire upgrade e downgrade del database.
  version: 1,
);

// Definisci una funzione che inserisce le persone nel database
Future<void> insertPersona(Persona persona) async {
  // Ottieni un riferimento al database.
  final db = await database;

  // Inserisci la Persona nella tabella corretta. Puoi anche specificare
  // il `conflictAlgorithm` da utilizzare in caso la persona sia inserita due volte.
  //
  // In questo caso, sostituisci tutti i dati precedenti.
  await db.insert(
    'persone',
    persona.toMap(),
    conflictAlgorithm: ConflictAlgorithm.replace,
  );
}

// Un metodo che recupera tutte le persone dalla tabella persone.
Future<List<Persona>> persone() async {
  // Ottieni un riferimento al database.
  final db = await database;

  // Esegui una query nella tabella per tutte le Persone.
  final List<Map<String, dynamic>> maps = await db.query('persone');

  // Converti la List<Map<String, dynamic> in List<Persona>.
  return List.generate(maps.length, (i) {
    return Persona(
      id: maps[i]['id'],
      nome: maps[i]['nome'],
      eta: maps[i]['eta'],
    );
  });
}

Future<void> updatePersona(Persona persona) async {
  // Ottieni un riferimento al database.
  final db = await database;

  // Aggiorna la persona.
  await db.update(
    'persone',
    persona.toMap(),
    // Assicurati che la persona abbia un ID corrispondente.
    where: 'id = ?',
    // Passa l’ ID della Persona come whereArg per evitare SQL injection.
    whereArgs: [persona.id],
  );
}

Future<void> deletePerson(int id) async {
  // Ottieni un riferimento al database.
  final db = await database;

  // Rimuovi la Persona dal database.
  await db.delete(
    'persone',
    // Utilizza una clausola `where` per eliminare dal db una persona specifica
    where: 'id = ?',
    // Passa l’id della Persona come whereArg per prevenire SQL injection.
    whereArgs: [id],
  );
}

// Crea una Persona e aggiungila alla tabella delle persone
var luca = Persona(
  id: 0,
  nome: 'Luca',
  eta: 30,
);

await insertPersona(luca);

// Utilizza il metodo indicato sopra per recuperare tutte le persone.
print(await persone());
// Stampa un elenco che include Luca.

// Aggiorna l’età di Luca e salvala nel database.
luca = Persona(
  id: luca.id,
  nome: luca.nome,
  eta: luca.eta + 7,
);
await updatePersona(luca);

// Stampa i risultati aggiornati.
print(await persone()); // Stampa Luca con l’eta di 37.

  // Rimuovi Luca dal database.
  await deletePerson(luca.id);

  // Stampa l'elenco delle persone (vuoto).
  print(await persone());
}

class Persona {
  final int id;
  final String nome;
  final int eta;

  Persona({
    required this.id,
    required this.nome,
    required this.eta,
  });

  // Converti una Persona in una Map. Le chiavi devono essere uguali ai nomi delle
  //colonne nel database.
  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'nome': nome,
      'eta': eta,
    };
  }

  // Implementa toString per rendere piu semplice visualizzare informazioni su
  // ogni persona quando viene utilizzato il comando print.
  @override
  String toString() {
    return 'Persona{id: $id, nome: $nome, eta: $eta}';
  }
}