Flutter - Conceptos de bases de datos

Flutter proporciona muchos paquetes avanzados para trabajar con bases de datos. Los paquetes más importantes son:

  • sqflite - Se utiliza para acceder y manipular la base de datos SQLite, y

  • firebase_database - Se utiliza para acceder y manipular la base de datos NoSQL alojada en la nube de Google.

En este capítulo, analicemos cada uno de ellos en detalle.

SQLite

La base de datos SQLite es el motor de base de datos integrado basado en SQL estándar y de facto. Es un motor de base de datos pequeño y probado en el tiempo. El paquete sqflite proporciona muchas funciones para trabajar de manera eficiente con la base de datos SQLite. Proporciona métodos estándar para manipular el motor de base de datos SQLite. La funcionalidad principal proporcionada por el paquete sqflite es la siguiente:

  • Cree / abra (método openDatabase) una base de datos SQLite.

  • Ejecutar declaración SQL (método de ejecución) contra la base de datos SQLite.

  • Métodos de consulta avanzados (método de consulta) para reducir al código necesario para consultar y obtener información de la base de datos SQLite.

Creemos una aplicación de producto para almacenar y obtener información del producto de un motor de base de datos SQLite estándar utilizando el paquete sqflite y comprender el concepto detrás de la base de datos SQLite y el paquete sqflite.

  • Cree una nueva aplicación Flutter en Android Studio, product_sqlite_app.

  • Reemplace el código de inicio predeterminado (main.dart) con nuestro código product_rest_app .

  • Copie la carpeta de activos de product_nav_app a product_rest_app y agregue activos dentro del archivo * pubspec.yaml`.

flutter: 
   assets: 
      - assets/appimages/floppy.png 
      - assets/appimages/iphone.png 
      - assets/appimages/laptop.png 
      - assets/appimages/pendrive.png 
      - assets/appimages/pixel.png 
      - assets/appimages/tablet.png
  • Configure el paquete sqflite en el archivo pubspec.yaml como se muestra a continuación:

dependencies: sqflite: any

Utilice el número de versión más reciente de sqflite en lugar de cualquier

  • Configure el paquete path_provider en el archivo pubspec.yaml como se muestra a continuación:

dependencies: path_provider: any
  • Aquí, el paquete path_provider se usa para obtener la ruta de la carpeta temporal del sistema y la ruta de la aplicación. Utilice el número de versión más reciente de sqflite en lugar de cualquier .

  • Android Studio alertará de que pubspec.yaml está actualizado.

  • Haga clic en la opción Obtener dependencias. Android Studio obtendrá el paquete de Internet y lo configurará correctamente para la aplicación.

  • En la base de datos, necesitamos la clave principal, id como campo adicional junto con las propiedades del producto como nombre, precio, etc. Entonces, agregue la propiedad id en la clase Product. Además, agregue un nuevo método, toMap para convertir el objeto del producto en el objeto Map. fromMap y toMap se utilizan para serializar y deserializar el objeto Producto y se utiliza en métodos de manipulación de bases de datos.

class Product { 
   final int id; 
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   static final columns = ["id", "name", "description", "price", "image"]; 
   Product(this.id, this.name, this.description, this.price, this.image); 
   factory Product.fromMap(Map<String, dynamic> data) {
      return Product( 
         data['id'], 
         data['name'], 
         data['description'], 
         data['price'], 
         data['image'], 
      ); 
   } 
   Map<String, dynamic> toMap() => {
      "id": id, 
      "name": name, 
      "description": description, 
      "price": price, 
      "image": image 
   }; 
}
  • Cree un nuevo archivo, Database.dart en la carpeta lib para escribir la funcionalidad relacionada con SQLite .

  • Importe la declaración de importación necesaria en Database.dart.

import 'dart:async'; 
import 'dart:io'; 
import 'package:path/path.dart'; 
import 'package:path_provider/path_provider.dart'; 
import 'package:sqflite/sqflite.dart'; 
import 'Product.dart';
  • Tenga en cuenta los siguientes puntos aquí:

    • async se utiliza para escribir métodos asincrónicos.

    • io se utiliza para acceder a archivos y directorios.

    • path se utiliza para acceder a la función de utilidad básica de dart relacionada con las rutas de archivo.

    • path_provider se utiliza para obtener una ruta temporal y de la aplicación.

    • sqflite se utiliza para manipular la base de datos SQLite.

  • Crea una nueva clase SQLiteDbProvider

  • Declare un objeto SQLiteDbProvider estático basado en singleton como se especifica a continuación:

class SQLiteDbProvider { 
   SQLiteDbProvider._(); 
   static final SQLiteDbProvider db = SQLiteDbProvider._(); 
   static Database _database; 
}
  • Se puede acceder al objeto SQLiteDBProvoider y su método a través de la variable db estática.

SQLiteDBProvoider.db.<emthod>
  • Cree un método para obtener la base de datos (opción Future) de tipo Future <Database>. Cree la tabla de productos y cargue los datos iniciales durante la creación de la propia base de datos.

Future<Database> get database async { 
   if (_database != null) 
   return _database; 
   _database = await initDB(); 
   return _database; 
}
initDB() async { 
   Directory documentsDirectory = await getApplicationDocumentsDirectory(); 
   String path = join(documentsDirectory.path, "ProductDB.db"); 
   return await openDatabase(
      path, 
      version: 1,
      onOpen: (db) {}, 
      onCreate: (Database db, int version) async {
         await db.execute(
            "CREATE TABLE Product ("
            "id INTEGER PRIMARY KEY,"
            "name TEXT,"
            "description TEXT,"
            "price INTEGER," 
            "image TEXT" ")"
         ); 
         await db.execute(
            "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [1, "iPhone", "iPhone is the stylist phone ever", 1000, "iphone.png"]
         ); 
         await db.execute(
            "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [2, "Pixel", "Pixel is the most feature phone ever", 800, "pixel.png"]
         ); 
         await db.execute(
            "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [3, "Laptop", "Laptop is most productive development tool", 2000, "laptop.png"]\
         ); 
         await db.execute( 
            "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [4, "Tablet", "Laptop is most productive development tool", 1500, "tablet.png"]
         );
         await db.execute( 
            "INSERT INTO Product 
            ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [5, "Pendrive", "Pendrive is useful storage medium", 100, "pendrive.png"]
         );
         await db.execute( 
            "INSERT INTO Product 
            ('id', 'name', 'description', 'price', 'image') 
            values (?, ?, ?, ?, ?)", 
            [6, "Floppy Drive", "Floppy drive is useful rescue storage medium", 20, "floppy.png"]
         ); 
      }
   ); 
}
  • Aquí, hemos utilizado los siguientes métodos:

    • getApplicationDocumentsDirectory - Devuelve la ruta del directorio de la aplicación

    • join- Se utiliza para crear una ruta específica del sistema. Lo hemos usado para crear una ruta de base de datos.

    • openDatabase - Se utiliza para abrir una base de datos SQLite

    • onOpen - Se usa para escribir código al abrir una base de datos

    • onCreate - Se utiliza para escribir código mientras se crea una base de datos por primera vez

    • db.execute- Se utiliza para ejecutar consultas SQL. Acepta una consulta. Si la consulta tiene un marcador de posición (?), Entonces acepta valores como lista en el segundo argumento.

  • Escriba un método para obtener todos los productos en la base de datos:

Future<List<Product>> getAllProducts() async { 
   final db = await database; 
   List<Map> 
   results = await db.query("Product", columns: Product.columns, orderBy: "id ASC"); 
   
   List<Product> products = new List(); 
   results.forEach((result) { 
      Product product = Product.fromMap(result); 
      products.add(product); 
   }); 
   return products; 
}
  • Aquí, hemos hecho lo siguiente:

    • Se utilizó el método de consulta para obtener toda la información del producto. query proporciona un acceso directo para consultar la información de una tabla sin escribir la consulta completa. El método de consulta generará la consulta adecuada mediante el uso de nuestra entrada como columnas, orderBy, etc.

    • Se usó el método fromMap de Product para obtener detalles del producto haciendo un bucle en el objeto de resultados, que contiene todas las filas de la tabla.

  • Escriba un método para obtener productos específicos para id

Future<Product> getProductById(int id) async {
   final db = await database; 
   var result = await db.query("Product", where: "id = ", whereArgs: [id]); 
   return result.isNotEmpty ? Product.fromMap(result.first) : Null; 
}
  • Aquí, hemos utilizado where y whereArgs para aplicar filtros.

  • Cree tres métodos: insertar, actualizar y eliminar el método para insertar, actualizar y eliminar el producto de la base de datos.

insert(Product product) async { 
   final db = await database; 
   var maxIdResult = await db.rawQuery(
      "SELECT MAX(id)+1 as last_inserted_id FROM Product");

   var id = maxIdResult.first["last_inserted_id"]; 
   var result = await db.rawInsert(
      "INSERT Into Product (id, name, description, price, image)" 
      " VALUES (?, ?, ?, ?, ?)", 
      [id, product.name, product.description, product.price, product.image] 
   ); 
   return result; 
}
update(Product product) async { 
   final db = await database; 
   var result = await db.update("Product", product.toMap(), 
   where: "id = ?", whereArgs: [product.id]); return result; 
} 
delete(int id) async { 
   final db = await database; 
   db.delete("Product", where: "id = ?", whereArgs: [id]); 
}
  • El código final de Database.dart es el siguiente:

import 'dart:async'; 
import 'dart:io'; 
import 'package:path/path.dart'; 
import 'package:path_provider/path_provider.dart'; 
import 'package:sqflite/sqflite.dart'; 
import 'Product.dart'; 

class SQLiteDbProvider {
   SQLiteDbProvider._(); 
   static final SQLiteDbProvider db = SQLiteDbProvider._(); 
   static Database _database; 
   
   Future<Database> get database async {
      if (_database != null) 
      return _database; 
      _database = await initDB(); 
      return _database; 
   } 
   initDB() async {
      Directory documentsDirectory = await 
      getApplicationDocumentsDirectory(); 
      String path = join(documentsDirectory.path, "ProductDB.db"); 
      return await openDatabase(
         path, version: 1, 
         onOpen: (db) {}, 
         onCreate: (Database db, int version) async {
            await db.execute(
               "CREATE TABLE Product (" 
               "id INTEGER PRIMARY KEY," 
               "name TEXT," 
               "description TEXT," 
               "price INTEGER," 
               "image TEXT"")"
            ); 
            await db.execute(
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [1, "iPhone", "iPhone is the stylist phone ever", 1000, "iphone.png"]
            ); 
            await db.execute( 
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [2, "Pixel", "Pixel is the most feature phone ever", 800, "pixel.png"]
            );
            await db.execute(
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [3, "Laptop", "Laptop is most productive development tool", 2000, "laptop.png"]
            ); 
            await db.execute( 
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [4, "Tablet", "Laptop is most productive development tool", 1500, "tablet.png"]
            ); 
            await db.execute( 
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [5, "Pendrive", "Pendrive is useful storage medium", 100, "pendrive.png"]
            );
            await db.execute( 
               "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') 
               values (?, ?, ?, ?, ?)", 
               [6, "Floppy Drive", "Floppy drive is useful rescue storage medium", 20, "floppy.png"]
            ); 
         }
      ); 
   }
   Future<List<Product>> getAllProducts() async {
      final db = await database; 
      List<Map> results = await db.query(
         "Product", columns: Product.columns, orderBy: "id ASC"
      ); 
      List<Product> products = new List();   
      results.forEach((result) {
         Product product = Product.fromMap(result); 
         products.add(product); 
      }); 
      return products; 
   } 
   Future<Product> getProductById(int id) async {
      final db = await database; 
      var result = await db.query("Product", where: "id = ", whereArgs: [id]); 
      return result.isNotEmpty ? Product.fromMap(result.first) : Null; 
   } 
   insert(Product product) async { 
      final db = await database; 
      var maxIdResult = await db.rawQuery("SELECT MAX(id)+1 as last_inserted_id FROM Product"); 
      var id = maxIdResult.first["last_inserted_id"]; 
      var result = await db.rawInsert(
         "INSERT Into Product (id, name, description, price, image)" 
         " VALUES (?, ?, ?, ?, ?)", 
         [id, product.name, product.description, product.price, product.image] 
      ); 
      return result; 
   } 
   update(Product product) async { 
      final db = await database; 
      var result = await db.update(
         "Product", product.toMap(), where: "id = ?", whereArgs: [product.id]
      ); 
      return result; 
   } 
   delete(int id) async { 
      final db = await database; 
      db.delete("Product", where: "id = ?", whereArgs: [id]);
   } 
}
  • Cambie el método principal para obtener la información del producto.

void main() {
   runApp(MyApp(products: SQLiteDbProvider.db.getAllProducts())); 
}
  • Aquí, hemos utilizado el método getAllProducts para buscar todos los productos de la base de datos.

  • Ejecute la aplicación y vea los resultados. Será similar al ejemplo anterior, Acceso a la API del servicio del producto , excepto que la información del producto se almacena y se obtiene de la base de datos SQLite local.

Cloud Firestore

Firebase es una plataforma de desarrollo de aplicaciones BaaS. Proporciona muchas funciones para acelerar el desarrollo de aplicaciones móviles, como el servicio de autenticación, el almacenamiento en la nube, etc. Una de las características principales de Firebase es Cloud Firestore, una base de datos NoSQL en tiempo real basada en la nube.

Flutter proporciona un paquete especial, cloud_firestore para programar con Cloud Firestore. Creemos una tienda de productos en línea en Cloud Firestore y creemos una aplicación para acceder a la tienda de productos.

  • Cree una nueva aplicación Flutter en Android Studio, product_firebase_app.

  • Reemplace el código de inicio predeterminado (main.dart) con nuestro código product_rest_app .

  • Copie el archivo Product.dart de product_rest_app en la carpeta lib.

class Product { 
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   
   Product(this.name, this.description, this.price, this.image); 
   factory Product.fromMap(Map<String, dynamic> json) {
      return Product( 
         json['name'], 
         json['description'], 
         json['price'], 
         json['image'], 
      ); 
   }
}
  • Copie la carpeta de activos de product_rest_app a product_firebase_app y agregue activos dentro del archivo pubspec.yaml.

flutter:
   assets: 
   - assets/appimages/floppy.png 
   - assets/appimages/iphone.png 
   - assets/appimages/laptop.png 
   - assets/appimages/pendrive.png 
   - assets/appimages/pixel.png 
   - assets/appimages/tablet.png
  • Configure el paquete cloud_firestore en el archivo pubspec.yaml como se muestra a continuación:

dependencies: cloud_firestore: ^0.9.13+1
  • Aquí, use la última versión del paquete cloud_firestore.

  • Android Studio alertará que pubspec.yaml está actualizado como se muestra aquí -

  • Haga clic en la opción Obtener dependencias. Android Studio obtendrá el paquete de Internet y lo configurará correctamente para la aplicación.

  • Crea un proyecto en Firebase siguiendo los siguientes pasos:

    • Cree una cuenta de Firebase seleccionando Plan gratuito en https://firebase.google.com/pricing/.

    • Una vez que se crea la cuenta de Firebase, se redireccionará a la página de descripción general del proyecto. Enumera todos los proyectos basados ​​en Firebase y proporciona una opción para crear un nuevo proyecto.

    • Haga clic en Agregar proyecto y se abrirá una página de creación de proyectos.

    • Ingrese la base de datos de la aplicación de productos como nombre del proyecto y haga clic en la opción Crear proyecto.

    • Vaya a * Firebase console.

    • Haga clic en Descripción general del proyecto. Abre la página de descripción general del proyecto.

    • Haga clic en el icono de Android. Abrirá la configuración del proyecto específica para el desarrollo de Android.

    • Ingrese el nombre del paquete de Android, com.tutorialspoint.flutterapp.product_firebase_app.

    • Haga clic en Registrar aplicación. Genera un archivo de configuración del proyecto, google_service.json.

    • Descargue google_service.json y luego muévalo al directorio android / app del proyecto. Este archivo es la conexión entre nuestra aplicación y Firebase.

    • Abra android / app / build.gradle e incluya el siguiente código:

apply plugin: 'com.google.gms.google-services'
    • Abra android / build.gradle e incluya la siguiente configuración:

buildscript {
   repositories { 
      // ... 
   } 
   dependencies { 
      // ... 
      classpath 'com.google.gms:google-services:3.2.1' // new 
   } 
}

    Aquí, el complemento y la ruta de clases se utilizan con el fin de leer el archivo google_service.json.

    • Abra android / app / build.gradle e incluya también el siguiente código.

android {
   defaultConfig { 
      ... 
      multiDexEnabled true 
   } 
   ...
}
dependencies {
   ... 
   compile 'com.android.support: multidex:1.0.3' 
}

    Esta dependencia permite que la aplicación de Android utilice varias funciones dex.

    • Siga los pasos restantes en Firebase Console o simplemente omítelo.

  • Cree una tienda de productos en el proyecto recién creado siguiendo los siguientes pasos:

    • Ve a la consola de Firebase.

    • Abra el proyecto recién creado.

    • Haga clic en la opción Base de datos en el menú de la izquierda.

    • Haga clic en la opción Crear base de datos.

    • Haga clic en Iniciar en modo de prueba y luego en Activar.

    • Haz clic en Agregar colección. Ingrese el producto como nombre de la colección y luego haga clic en Siguiente.

    • Ingrese la información del producto de muestra como se muestra en la imagen aquí -

  • Agregue información adicional sobre el producto usando las opciones Agregar documento .

  • Abra el archivo main.dart e importe el archivo del complemento de Cloud Firestore y elimine el paquete http.

import 'package:cloud_firestore/cloud_firestore.dart';
  • Quite parseProducts y actualice fetchProducts para obtener productos de Cloud Firestore en lugar de la API de servicio de productos.

Stream<QuerySnapshot> fetchProducts() { 
   return Firestore.instance.collection('product').snapshots(); }
  • Aquí, el método Firestore.instance.collection se utiliza para acceder a la colección de productos disponible en la tienda en la nube. Firestore.instance.collection ofrece muchas opciones para filtrar la colección y obtener los documentos necesarios. Pero no hemos aplicado ningún filtro para obtener toda la información del producto.

  • Cloud Firestore proporciona la colección a través del concepto de Dart Stream y, por lo tanto, modifica el tipo de productos en el widget MyApp y MyHomePage de Future <list <Product>> a Stream <QuerySnapshot>.

  • Cambie el método de construcción del widget MyHomePage para usar StreamBuilder en lugar de FutureBuilder.

@override 
Widget build(BuildContext context) {
   return Scaffold(
      appBar: AppBar(title: Text("Product Navigation")), 
      body: Center(
         child: StreamBuilder<QuerySnapshot>(
            stream: products, builder: (context, snapshot) {
               if (snapshot.hasError) print(snapshot.error); 
               if(snapshot.hasData) {
                  List<DocumentSnapshot> 
                  documents = snapshot.data.documents; 
                  
                  List<Product> 
                  items = List<Product>(); 
                  
                  for(var i = 0; i < documents.length; i++) { 
                     DocumentSnapshot document = documents[i]; 
                     items.add(Product.fromMap(document.data)); 
                  } 
                  return ProductBoxList(items: items);
               } else { 
                  return Center(child: CircularProgressIndicator()); 
               }
            }, 
         ), 
      )
   ); 
}
  • Aquí, hemos obtenido la información del producto como tipo List <DocumentSnapshot>. Dado que nuestro widget, ProductBoxList no es compatible con documentos, hemos convertido los documentos en el tipo List <Product> y lo hemos utilizado más.

  • Finalmente, ejecute la aplicación y vea el resultado. Dado que hemos utilizado la misma información de producto que la de la aplicación SQLite y solo hemos cambiado el medio de almacenamiento, la aplicación resultante parece idéntica a la aplicación SQLite .