Curso de Integración Módulos Nativos iOS/Android para Flutter

Configuración de modelos TensorFlow Lite en Flutter

Curso de Integración Módulos Nativos iOS/Android para Flutter

Contenido del curso

Acceso a sensores y cámara

Configuración de modelos TensorFlow Lite en Flutter

Resumen

Integrar un modelo de inteligencia artificial en una app móvil parece complejo, pero con TensorFlow Lite y Flutter puedes hacerlo en pocos pasos. Aquí aprenderás a configurar tu proyecto, cargar un modelo .tflite con sus labels y ejecutar la inferencia desde un servicio en Dart, listo para conectarse luego a tu interfaz.

¿Cómo organizo los archivos del modelo dentro del proyecto Flutter?

El primer paso es darle a tu proyecto un lugar claro donde vivan el modelo y sus etiquetas. Sin esa estructura, el interpreter no sabrá qué cargar.

Descarga dos archivos desde el enlace de recursos: el modelo en formato .tflite y el archivo labels.txt. Luego, en la raíz de tu proyecto, crea una carpeta llamada assets y dentro otra llamada models. Pega ambos archivos ahí [01:05].

¿Cómo declaro los assets en el archivo pubspec.yaml?

Abre tu pubspec.yaml y busca con Control F la sección de assets. Descomenta esa parte y agrega la ruta exacta a cada archivo:

yaml assets:

  • assets/models/model.tflite
  • assets/models/labels.txt

Guarda el archivo y ejecuta flutter pub get desde la consola para que Flutter registre esos recursos dentro del bundle de la app [02:30].

¿Qué pasa si no declaro los assets en pubspec.yaml? El interpreter no encontrará el modelo en tiempo de ejecución y verás un error de archivo no encontrado, aunque los archivos existan en la carpeta.

¿Cómo creo un servicio para usar TensorFlow Lite en Flutter?

La lógica de carga e inferencia debe vivir aislada de la UI. Por eso creas una clase de servicio que centralice todo lo relacionado con el modelo.

Dentro de tu carpeta principal, crea un archivo service.dart y define la clase TensorFlowService. Importa la librería con:

dart import 'package:tflite_flutter/tflite_flutter.dart';

Declara una propiedad Interpreter que será la instancia encargada de comunicarse con tu modelo a lo largo de toda la app [04:15].

¿Cómo cargo el modelo con la función loadModel?

Crea una función asíncrona Future<void> loadModel() y envuélvela en un try catch. Dentro del try, usa el método Interpreter.fromAsset para apuntar a la ruta assets/models/model.tflite.

dart interpreter = await Interpreter.fromAsset('assets/models/model.tflite'); print('Modelo cargado');

Después valida con un if (interpreter != null) y captura las dimensiones de entrada y salida con getInputTensor(0).shape y getOutputTensor(0).shape. Imprimirlas te ayuda a entender qué espera tu modelo y qué devolverá [06:40].

¿Cómo ejecuto la inferencia con la función runModel?

Una vez cargado el modelo, necesitas una función que reciba datos y devuelva una predicción. Esa es la segunda pieza del servicio.

Define Future<List<double>> runModel() async y, antes de ejecutar nada, valida que el interpreter no sea nulo. Si lo es, retorna una lista vacía para evitar que la app crashee.

¿Por qué retornar una lista vacía si el modelo no cargó? Porque la función promete devolver List<double>. Devolver una lista vacía mantiene el contrato del tipo de dato y permite a la UI manejar el error sin excepciones.

¿Qué representa el input que recibe el modelo?

El input es una matriz que simula una imagen. En este ejemplo se construye manualmente con cuatro dimensiones:

  • Número de imagen: cuántas imágenes envías al modelo en un batch.
  • Altura: alto en píxeles de la imagen.
  • Ancho: ancho en píxeles de la imagen.
  • Canales RGB: los tres valores de color (rojo, verde, azul) por cada píxel.

Es decir, le entregas al modelo lo mismo que recibiría si tomaras una foto desde el celular, pero generado desde código para esta primera prueba [11:20].

¿Cómo construyo el output esperado?

La salida también es una matriz preinicializada. Se crea con List.filled y un reshape para que coincida con el formato que el modelo entrega:

dart var output = List.filled(1 * 1001, 0).reshape([1, 1001]);

Ese 1001 representa las categorías que el modelo puede reconocer. Cada posición almacenará una probabilidad de coincidencia entre tu imagen y una categoría. Inicializarlo en cero permite que el modelo haga la comparación y rellene los valores reales.

Finalmente, dentro del try, llamas a interpreter.run(input, output) y retornas output[0]. En el catch, imprimes el error y devuelves una lista vacía para mantener la consistencia [13:45].

¿Por qué separar carga e inferencia en funciones distintas?

Dividir loadModel y runModel te da control sobre el ciclo de vida del modelo. Cargas una sola vez al iniciar la app y ejecutas la inferencia tantas veces como necesites sin volver a leer el archivo desde assets.

Esto mejora el rendimiento, evita lecturas redundantes de disco y hace tu código más fácil de testear. Cada función tiene una responsabilidad clara: una prepara, la otra predice.

Con este servicio listo, la siguiente clase conectará la UI para enviar una imagen real y ver cómo el modelo responde con su predicción. ¿Ya pensaste qué tipo de imágenes vas a probar primero?