Introducción
SQLite es una biblioteca de software que proporciona una base de datos relacional ligera y autónoma. Es una opción popular para el almacenamiento local en aplicaciones móviles debido a su simplicidad y eficiencia. En este tema, aprenderás cómo integrar SQLite en tu aplicación Flutter para gestionar datos de manera persistente.
Objetivos de Aprendizaje
- Comprender qué es SQLite y por qué es útil en aplicaciones móviles.
- Configurar SQLite en un proyecto Flutter.
- Crear, leer, actualizar y eliminar (CRUD) datos en una base de datos SQLite.
- Manejar errores comunes y optimizar el rendimiento de las operaciones de base de datos.
Configuración de SQLite en Flutter
Para utilizar SQLite en Flutter, usaremos el paquete sqflite
. Sigue estos pasos para configurarlo:
-
Agregar Dependencias: Abre el archivo
pubspec.yaml
y añade las siguientes dependencias:dependencies: flutter: sdk: flutter sqflite: ^2.0.0+3 path: ^1.8.0
-
Instalar Dependencias: Ejecuta el comando
flutter pub get
para instalar las dependencias. -
Importar Paquetes: Importa los paquetes necesarios en tu archivo Dart:
import 'package:sqflite/sqflite.dart'; import 'package:path/path.dart';
Creación de la Base de Datos
Paso 1: Inicializar la Base de Datos
Crea una función para inicializar la base de datos. Esta función abrirá la base de datos y ejecutará una consulta SQL para crear una tabla si no existe.
Future<Database> initializeDB() async { String path = await getDatabasesPath(); return openDatabase( join(path, 'example.db'), onCreate: (database, version) async { await database.execute( "CREATE TABLE items(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, quantity INTEGER NOT NULL)", ); }, version: 1, ); }
Paso 2: Insertar Datos
Crea una función para insertar datos en la tabla.
Future<void> insertItem(Database db, String name, int quantity) async { await db.insert( 'items', {'name': name, 'quantity': quantity}, conflictAlgorithm: ConflictAlgorithm.replace, ); }
Paso 3: Leer Datos
Crea una función para leer datos de la tabla.
Future<List<Map<String, dynamic>>> retrieveItems(Database db) async { final List<Map<String, dynamic>> queryResult = await db.query('items'); return queryResult; }
Paso 4: Actualizar Datos
Crea una función para actualizar datos en la tabla.
Future<void> updateItem(Database db, int id, String name, int quantity) async { await db.update( 'items', {'name': name, 'quantity': quantity}, where: "id = ?", whereArgs: [id], ); }
Paso 5: Eliminar Datos
Crea una función para eliminar datos de la tabla.
Future<void> deleteItem(Database db, int id) async { await db.delete( 'items', where: "id = ?", whereArgs: [id], ); }
Ejemplo Completo
A continuación, se muestra un ejemplo completo que integra todas las funciones anteriores en una aplicación Flutter.
import 'package:flutter/material.dart'; import 'package:sqflite/sqflite.dart'; import 'package:path/path.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: HomeScreen(), ); } } class HomeScreen extends StatefulWidget { @override _HomeScreenState createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { Database? _database; @override void initState() { super.initState(); initializeDB().then((db) { setState(() { _database = db; }); }); } Future<Database> initializeDB() async { String path = await getDatabasesPath(); return openDatabase( join(path, 'example.db'), onCreate: (database, version) async { await database.execute( "CREATE TABLE items(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, quantity INTEGER NOT NULL)", ); }, version: 1, ); } Future<void> insertItem(String name, int quantity) async { await _database?.insert( 'items', {'name': name, 'quantity': quantity}, conflictAlgorithm: ConflictAlgorithm.replace, ); setState(() {}); } Future<List<Map<String, dynamic>>> retrieveItems() async { final List<Map<String, dynamic>> queryResult = await _database!.query('items'); return queryResult; } Future<void> updateItem(int id, String name, int quantity) async { await _database?.update( 'items', {'name': name, 'quantity': quantity}, where: "id = ?", whereArgs: [id], ); setState(() {}); } Future<void> deleteItem(int id) async { await _database?.delete( 'items', where: "id = ?", whereArgs: [id], ); setState(() {}); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('SQLite Example'), ), body: FutureBuilder( future: retrieveItems(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { return Center(child: Text('Error: ${snapshot.error}')); } else { final items = snapshot.data as List<Map<String, dynamic>>; return ListView.builder( itemCount: items.length, itemBuilder: (context, index) { return ListTile( title: Text(items[index]['name']), subtitle: Text('Quantity: ${items[index]['quantity']}'), trailing: IconButton( icon: Icon(Icons.delete), onPressed: () { deleteItem(items[index]['id']); }, ), ); }, ); } }, ), floatingActionButton: FloatingActionButton( onPressed: () { insertItem('Item ${DateTime.now()}', 1); }, child: Icon(Icons.add), ), ); } }
Ejercicios Prácticos
- Ejercicio 1: Modifica la aplicación para que permita actualizar la cantidad de un ítem existente.
- Ejercicio 2: Añade una funcionalidad para buscar ítems por nombre.
- Ejercicio 3: Implementa una función para ordenar los ítems por cantidad.
Soluciones
Ejercicio 1: Modificar la cantidad de un ítem existente.
// Añadir un botón de edición en cada ListTile trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: Icon(Icons.edit), onPressed: () { updateItem(items[index]['id'], items[index]['name'], items[index]['quantity'] + 1); }, ), IconButton( icon: Icon(Icons.delete), onPressed: () { deleteItem(items[index]['id']); }, ), ], ),
Ejercicio 2: Buscar ítems por nombre.
Future<List<Map<String, dynamic>>> searchItems(String name) async { final List<Map<String, dynamic>> queryResult = await _database!.query( 'items', where: "name LIKE ?", whereArgs: ['%$name%'], ); return queryResult; }
Ejercicio 3: Ordenar los ítems por cantidad.
Future<List<Map<String, dynamic>>> retrieveItemsOrdered() async { final List<Map<String, dynamic>> queryResult = await _database!.query( 'items', orderBy: "quantity DESC", ); return queryResult; }
Conclusión
En esta sección, has aprendido cómo integrar SQLite en una aplicación Flutter para realizar operaciones CRUD. SQLite es una herramienta poderosa para el almacenamiento local y es esencial para aplicaciones que requieren persistencia de datos. Asegúrate de manejar adecuadamente los errores y optimizar las consultas para mantener un rendimiento óptimo.
Curso de Desarrollo con Flutter
Módulo 1: Introducción a Flutter
- ¿Qué es Flutter?
- Configuración del Entorno de Desarrollo
- Entendiendo la Arquitectura de Flutter
- Creando Tu Primera App con Flutter
Módulo 2: Conceptos Básicos de Programación en Dart
- Introducción a Dart
- Variables y Tipos de Datos
- Sentencias de Control de Flujo
- Funciones y Métodos
- Programación Orientada a Objetos en Dart
Módulo 3: Widgets en Flutter
- Introducción a los Widgets
- Widgets Stateless vs Stateful
- Widgets Básicos
- Widgets de Diseño
- Widgets de Entrada y Formularios
Módulo 4: Gestión de Estado
Módulo 5: Navegación y Enrutamiento
- Introducción a la Navegación
- Navegación Básica
- Rutas Nombradas
- Pasando Datos Entre Pantallas
- Deep Linking
Módulo 6: Redes y APIs
- Obteniendo Datos de Internet
- Parseo de Datos JSON
- Manejo de Errores de Red
- Usando APIs REST
- Integración con GraphQL
Módulo 7: Persistencia y Almacenamiento
- Introducción a la Persistencia
- Preferencias Compartidas
- Almacenamiento de Archivos
- Base de Datos SQLite
- Usando Hive para Almacenamiento Local
Módulo 8: Conceptos Avanzados de Flutter
- Animaciones en Flutter
- Custom Paint y Canvas
- Canales de Plataforma
- Isolates y Concurrencia
- Optimización de Rendimiento
Módulo 9: Pruebas y Depuración
- Introducción a las Pruebas
- Pruebas Unitarias
- Pruebas de Widgets
- Pruebas de Integración
- Técnicas de Depuración
Módulo 10: Despliegue y Mantenimiento
- Preparación para el Lanzamiento
- Construcción para iOS
- Construcción para Android
- Integración Continua/Despliegue Continuo (CI/CD)
- Mantenimiento y Actualización de Tu App