skills/flutter/machine-learning/SKILL.md
Machine Learning integration for Flutter using TensorFlow Lite and Firebase ML
npx skillsauth add altrupets/monorepo machine-learningInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill enables machine learning capabilities in Flutter applications using TensorFlow Lite and Firebase ML Kit. Supports image classification, object detection, text recognition, and custom model deployment.
dependencies:
tflite_flutter: ^0.10.4
tflite_flutter_helper: ^0.3.1
firebase_ml_model_downloader: ^0.2.4
google_mlkit_text_recognition: ^0.11.0
google_mlkit_face_detection: ^0.9.0
google_mlkit_barcode_scanning: ^0.10.0
google_mlkit_object_detection: ^0.11.0
image_picker: ^1.0.7
image: ^4.1.3
Add your TensorFlow Lite model to assets/models/:
# pubspec.yaml
flutter:
assets:
- assets/models/model.tflite
- assets/labels/labels.txt
import 'package:tflite_flutter/tflite_flutter.dart';
import 'package:image/image.dart' as img;
import 'dart:io';
class ImageClassifier {
Interpreter? _interpreter;
List<String>? _labels;
Future<void> loadModel() async {
try {
_interpreter = await Interpreter.fromAsset('assets/models/model.tflite');
// Load labels
final labelData = await rootBundle.loadString('assets/labels/labels.txt');
_labels = labelData.split('\n');
} catch (e) {
print('Error loading model: $e');
}
}
Future<Map<String, double>> classifyImage(File imageFile) async {
if (_interpreter == null) {
throw Exception('Model not loaded');
}
// Load and preprocess image
final imageData = await imageFile.readAsBytes();
var image = img.decodeImage(imageData)!;
// Resize to model input size (e.g., 224x224)
image = img.copyResize(image, width: 224, height: 224);
// Convert to input format
final input = _imageToInputList(image);
// Run inference
final output = List.filled(1 * _labels!.length, 0).reshape([1, _labels!.length]);
_interpreter!.run(input, output);
// Process results
final results = <String, double>{};
for (int i = 0; i < _labels!.length; i++) {
results[_labels![i]] = output[0][i];
}
return results;
}
List<List<List<List<double>>>> _imageToInputList(img.Image image) {
final input = List.generate(
1,
(_) => List.generate(
224,
(y) => List.generate(
224,
(x) {
final pixel = image.getPixel(x, y);
return [
pixel.r / 255.0,
pixel.g / 255.0,
pixel.b / 255.0,
];
},
),
),
);
return input;
}
void dispose() {
_interpreter?.close();
}
}
class ImageClassificationPage extends ConsumerStatefulWidget {
const ImageClassificationPage({super.key});
@override
ConsumerState<ImageClassificationPage> createState() => _ImageClassificationPageState();
}
class _ImageClassificationPageState extends ConsumerState<ImageClassificationPage> {
final ImageClassifier _classifier = ImageClassifier();
File? _selectedImage;
Map<String, double>? _results;
bool _isLoading = false;
@override
void initState() {
super.initState();
_classifier.loadModel();
}
Future<void> _pickImage() async {
final picker = ImagePicker();
final picked = await picker.pickImage(source: ImageSource.gallery);
if (picked != null) {
setState(() {
_selectedImage = File(picked.path);
_isLoading = true;
});
final results = await _classifier.classifyImage(_selectedImage!);
setState(() {
_results = results;
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Image Classification')),
body: Column(
children: [
if (_selectedImage != null)
Image.file(_selectedImage!, height: 300),
const SizedBox(height: 20),
if (_isLoading)
const CircularProgressIndicator()
else if (_results != null)
Expanded(
child: ListView.builder(
itemCount: _results!.length,
itemBuilder: (context, index) {
final label = _results!.keys.elementAt(index);
final confidence = _results![label]!;
return ListTile(
title: Text(label),
trailing: Text('${(confidence * 100).toStringAsFixed(1)}%'),
subtitle: LinearProgressIndicator(
value: confidence,
),
);
},
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: _pickImage,
child: const Icon(Icons.image),
),
);
}
@override
void dispose() {
_classifier.dispose();
super.dispose();
}
}
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
class TextRecognitionService {
final TextRecognizer _textRecognizer = TextRecognizer();
Future<String> recognizeText(File imageFile) async {
final inputImage = InputImage.fromFile(imageFile);
final recognizedText = await _textRecognizer.processImage(inputImage);
return recognizedText.text;
}
Future<List<TextBlock>> recognizeTextBlocks(File imageFile) async {
final inputImage = InputImage.fromFile(imageFile);
final recognizedText = await _textRecognizer.processImage(inputImage);
return recognizedText.blocks;
}
void dispose() {
_textRecognizer.close();
}
}
class OCRWidget extends StatefulWidget {
const OCRWidget({super.key});
@override
State<OCRWidget> createState() => _OCRWidgetState();
}
class _OCRWidgetState extends State<OCRWidget> {
final TextRecognitionService _service = TextRecognitionService();
File? _image;
String _recognizedText = '';
Future<void> _scanText() async {
final picker = ImagePicker();
final picked = await picker.pickImage(source: ImageSource.camera);
if (picked != null) {
setState(() => _image = File(picked.path));
final text = await _service.recognizeText(_image!);
setState(() => _recognizedText = text);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
children: [
if (_image != null)
Image.file(_image!, height: 200),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _scanText,
child: const Text('Scan Text'),
),
const SizedBox(height: 20),
Text(
'Recognized Text:',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 10),
Text(_recognizedText),
],
),
),
);
}
@override
void dispose() {
_service.dispose();
super.dispose();
}
}
import 'package:google_mlkit_object_detection/google_mlkit_object_detection.dart';
class ObjectDetectionService {
late ObjectDetector _objectDetector;
void initialize() {
final options = ObjectDetectorOptions(
mode: DetectionMode.single,
classifyObjects: true,
trackMultipleObjects: true,
);
_objectDetector = ObjectDetector(options: options);
}
Future<List<DetectedObject>> detectObjects(File imageFile) async {
final inputImage = InputImage.fromFile(imageFile);
return await _objectDetector.processImage(inputImage);
}
void dispose() {
_objectDetector.close();
}
}
class ObjectDetectionPainter extends CustomPainter {
final List<DetectedObject> objects;
final Size imageSize;
ObjectDetectionPainter({required this.objects, required this.imageSize});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 3.0
..color = Colors.green;
final textPainter = TextPainter(
textDirection: TextDirection.ltr,
);
for (final object in objects) {
final rect = _scaleRect(object.boundingBox, imageSize, size);
canvas.drawRect(rect, paint);
// Draw label
if (object.labels.isNotEmpty) {
final label = object.labels.first;
textPainter.text = TextSpan(
text: '${label.text} ${(label.confidence * 100).toStringAsFixed(0)}%',
style: const TextStyle(
color: Colors.green,
fontSize: 16,
fontWeight: FontWeight.bold,
backgroundColor: Colors.white,
),
);
textPainter.layout();
textPainter.paint(canvas, Offset(rect.left, rect.top - 20));
}
}
}
Rect _scaleRect(Rect rect, Size imageSize, Size canvasSize) {
final scaleX = canvasSize.width / imageSize.width;
final scaleY = canvasSize.height / imageSize.height;
return Rect.fromLTRB(
rect.left * scaleX,
rect.top * scaleY,
rect.right * scaleX,
rect.bottom * scaleY,
);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart';
class BarcodeScannerService {
final BarcodeScanner _scanner = BarcodeScanner();
Future<List<Barcode>> scanBarcodes(File imageFile) async {
final inputImage = InputImage.fromFile(imageFile);
return await _scanner.processImage(inputImage);
}
void dispose() {
_scanner.close();
}
}
// Real-time barcode scanning with camera
class BarcodeScannerPage extends StatefulWidget {
const BarcodeScannerPage({super.key});
@override
State<BarcodeScannerPage> createState() => _BarcodeScannerPageState();
}
class _BarcodeScannerPageState extends State<BarcodeScannerPage> {
CameraController? _cameraController;
final BarcodeScanner _scanner = BarcodeScanner();
bool _isProcessing = false;
String? _scannedBarcode;
@override
void initState() {
super.initState();
_initializeCamera();
}
Future<void> _initializeCamera() async {
final cameras = await availableCameras();
_cameraController = CameraController(
cameras.first,
ResolutionPreset.medium,
enableAudio: false,
);
await _cameraController!.initialize();
await _cameraController!.startImageStream(_processCameraImage);
setState(() {});
}
Future<void> _processCameraImage(CameraImage image) async {
if (_isProcessing) return;
_isProcessing = true;
try {
final WriteBuffer allBytes = WriteBuffer();
for (final Plane plane in image.planes) {
allBytes.putUint8List(plane.bytes);
}
final bytes = allBytes.done().buffer.asUint8List();
final imageSize = Size(image.width.toDouble(), image.height.toDouble());
final inputImage = InputImage.fromBytes(
bytes: bytes,
metadata: InputImageMetadata(
size: imageSize,
rotation: InputImageRotation.rotation0,
format: InputImageFormat.nv21,
bytesPerRow: image.planes.first.bytesPerRow,
),
);
final barcodes = await _scanner.processImage(inputImage);
if (barcodes.isNotEmpty) {
setState(() {
_scannedBarcode = barcodes.first.rawValue;
});
}
} finally {
_isProcessing = false;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Scan Barcode')),
body: Stack(
children: [
if (_cameraController != null)
CameraPreview(_cameraController!),
if (_scannedBarcode != null)
Center(
child: Container(
padding: const EdgeInsets.all(16),
color: Colors.black54,
child: Text(
'Scanned: $_scannedBarcode',
style: const TextStyle(color: Colors.white, fontSize: 20),
),
),
),
],
),
);
}
@override
void dispose() {
_cameraController?.dispose();
_scanner.close();
super.dispose();
}
}
import 'package:firebase_ml_model_downloader/firebase_ml_model_downloader.dart';
class FirebaseMLService {
Future<void> downloadModel(String modelName) async {
final model = await FirebaseModelDownloader.instance.getModel(
modelName,
FirebaseModelDownloadType.localModel,
FirebaseModelDownloadConditions(
iosAllowsCellularAccess: true,
iosAllowsBackgroundDownloading: false,
androidChargingRequired: false,
androidWifiRequired: false,
androidDeviceIdleRequired: false,
),
);
print('Model downloaded to: ${model.file}');
}
}
class OptimizedMLService {
Interpreter? _interpreter;
bool _isModelLoaded = false;
Future<void> loadModel() async {
if (_isModelLoaded) return;
final options = InterpreterOptions()
..threads = 4 // Use multiple threads
..useNnApiForAndroid = true; // Use NNAPI acceleration
_interpreter = await Interpreter.fromAsset(
'model.tflite',
options: options,
);
_isModelLoaded = true;
}
// Process multiple images in batch
Future<List<dynamic>> batchInference(List<File> images) async {
final results = <dynamic>[];
for (final image in images) {
final result = await _singleInference(image);
results.add(result);
}
return results;
}
Future<dynamic> _singleInference(File image) async {
// Preprocess and run inference
// ...
}
}
development
# 🔧 Skill: Native Integration (Swift/Kotlin) ## 📋 Metadata | Atributo | Valor | |----------|-------| | **ID** | `flutter-native-integration` | | **Nivel** | 🔴 Avanzado | | **Versión** | 1.0.0 | | **Keywords** | `native-integration`, `swift`, `kotlin`, `uikit`, `android-sdk`, `native-ui` | | **Referencia** | [Flutter Platform Integration](https://docs.flutter.dev/platform-integration) | ## 🔑 Keywords para Invocación Usa cualquiera de estos keywords en tus prompts para invocar este skill:
testing
# 🎨 Skill: MVVM Pattern ## 📋 Metadata | Atributo | Valor | |----------|-------| | **ID** | `flutter-mvvm-pattern` | | **Nivel** | 🟡 Intermedio | | **Versión** | 1.0.0 | | **Keywords** | `mvvm`, `model-view-viewmodel`, `provider`, `changenotifier` | ## 🔑 Keywords para Invocación Usa cualquiera de estos keywords en tus prompts para invocar este skill: - `mvvm` - `model-view-viewmodel` - `provider` - `changenotifier` - `@skill:mvvm` ### Ejemplos de Prompts ``` Crea una app de lista de ta
development
# 🎨 Skill: Arquitectura Modular ## 📋 Metadata | Atributo | Valor | |----------|-------| | **ID** | `flutter-modular-architecture` | | **Nivel** | 🔴 Avanzado | | **Versión** | 1.0.0 | | **Keywords** | `modular`, `modular-architecture`, `module`, `multi-module` | | **Referencia** | [Flutter Modular Package](https://pub.dev/packages/flutter_modular) | ## 🔑 Keywords para Invocación Usa cualquiera de estos keywords en tus prompts para invocar este skill: - `modular` - `modular-architecture`
tools
# 📱 Skill: Mobile Testing y Debugging con Flutter MCP ## 📋 Metadata | Atributo | Valor | |----------|-------| | **ID** | `flutter-mobile-testing` | | **Nivel** | 🔴 Avanzado | | **Versión** | 1.2.0 | | **Keywords** | `mobile-testing`, `integration-test`, `flutter-mcp`, `dart-mcp`, `debugging`, `logic-analysis`, `widget-inspection`, `device-testing` | | **Referencia** | [Dart and Flutter MCP server](https://docs.flutter.dev/ai/mcp-server) \| [Mobile MCP](https://github.com/mobile-next/mobile-