Receber por email
← Voltar para o catálogo

flutter-gemma-skill

Use this skill when building reusable AI capabilities on top of flutter_gemma. For developers who want to create packageable Skills with function calling, structured JSON output, system instructions, and result parsing. Covers: designing function schemas, implementing Skill classes, building execution engines, and publishing AI skill packages.

2estrelas
Atualizado há 6 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add houyongsheng/flutter_gemma_skill

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/flutter-gemma-skill/svg)](https://www.skillteca.com.br/skills/flutter-gemma-skill)

Alerta por categoria

Receba novas skills de Design e Frontend toda segunda

1 email curto, só com as skills novas de Design e Frontend. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Flutter Gemma Skill Development

Guide for wrapping flutter_gemma function calling capabilities into reusable, production-ready AI Skills.

What is a Skill?

A Skill is a self-contained AI capability package containing:

  • System Instruction - How the model should behave
  • Function Schema - JSON Schema for structured output
  • Result Parser - Converts model output to typed results
  • Prompt Builder - Constructs user input into model prompts

Skill vs Direct API Calls

// ❌ Direct calls - repetitive boilerplate
final session = await model.createSession(tools: [myTool]);
await session.addQueryChunk(Message.text(text: 'translate to French', isUser: true));
// ... parse JSON manually ...

// ✅ Using Skill - clean, reusable
final translationSkill = TranslationSkill();
final result = await translationSkill.execute(model, text: 'Hello');

Skill Architecture

Follow feature-based project structure for clean separation of concerns:

my_ai_skills/
├── lib/
│   ├── skill.dart                    # Skill base class & interfaces
│   ├── skill_result.dart             # Result types (immutable)
│   ├── skill_engine.dart            # Execution engine
│   └── features/
│       ├── translation/
│       │   ├── translation_skill.dart
│       │   ├── translation_schema.dart
│       │   └── translation_result.dart
│       └── summarization/
│           ├── summarization_skill.dart
│           ├── summarization_schema.dart
│           └── summarization_result.dart

Defining a Skill

Step 1: Define Result Types

// skill_result.dart

abstract class SkillResult {
  bool get success;
  String? get errorMessage;
}

class TranslationResult extends SkillResult {
  final String translatedText;
  final String sourceLanguage;
  final String targetLanguage;

  TranslationResult.success({
    required this.translatedText,
    required this.sourceLanguage,
    required this.targetLanguage,
  }) : success = true, errorMessage = null;

  TranslationResult.error(String message)
      : success = false,
        errorMessage = message,
        translatedText = '',
        sourceLanguage = '',
        targetLanguage = '';
}

class SummaryResult extends SkillResult {
  final String summary;
  final int wordCount;
  final List<String>? keyPoints;

  SummaryResult.success({
    required this.summary,
    required this.wordCount,
    this.keyPoints,
  }) : success = true, errorMessage = null;

  SummaryResult.error(String message)
      : success = false,
        errorMessage = message,
        summary = '',
        wordCount = 0,
        keyPoints = null;
}

Step 2: Define Function Schema

// schemas/translation_schema.dart

const translationFunctionSchema = {
  'name': 'translate_text',
  'description': 'Translate text between languages accurately',
  'parameters': {
    'type': 'object',
    'properties': {
      'translated_text': {
        'type': 'string',
        'description': 'The translated text',
      },
      'source_language': {
        'type': 'string',
        'description': 'Source language name or code',
      },
      'target_language': {
        'type': 'string',
        'description': 'Target language name or code',
      },
    },
    'required': ['translated_text', 'source_language', 'target_language'],
  },
};

Step 3: Implement Skill Class

// skills/translation_skill.dart

import 'dart:convert';
import 'package:flutter_gemma/flutter_gemma.dart';
import '../skill.dart';
import '../skill_result.dart';
import '../schemas/translation_schema.dart';

class TranslationSkill extends Skill {
  @override
  String get id => 'translation';

  @override
  String get name => 'Translator';

  @override
  String get description => 'Translate text between 140+ languages';

  @override
  String get systemInstruction => '''
You are an expert translator. Translate text accurately while preserving:
- Meaning and nuance
- Cultural context
- Technical terminology
- Tone and style
''';

  @override
  Map<String, dynamic>? get functionSchema => translationFunctionSchema;

  @override
  String buildPrompt(SkillInput input) {
    final targetLang = input.extra?['target_language'] ?? 'English';
    final sourceLang = input.extra?['source_language'];
    final prefix = sourceLang != null
        ? 'Translate from $sourceLang to $targetLang:'
        : 'Translate to $targetLang:';
    return '$prefix\n\n${input.text}\n\nUse the translate_text function.';
  }

  @override
  SkillResult parseResult(FunctionCall functionCall) {
    try {
      final args = jsonDecode(functionCall.argumentsText) as Map<String, dynamic>;
      return TranslationResult.success(
        translatedText: args['translated_text'] as String,
        sourceLanguage: args['source_language'] as String,
        targetLanguage: args['target_language'] as String,
      );
    } catch (e) {
      return TranslationResult.error('Parse error: $e');
    }
  }
}

Step 4: Create SkillEngine

// skill_engine.dart

import 'dart:convert';
import 'package:flutter_gemma/flutter_gemma.dart';
import 'skill.dart';
import 'skill_result.dart';

class SkillEngine {
  InferenceModel? _model;

  bool get isModelLoaded => _model != null;

  /// Execute a skill
  Future<SkillResult> execute({
    required Skill skill,
    required String input,
    Map<String, dynamic>? extra,
  }) async {
    if (_model == null) {
      throw StateError('Model not loaded');
    }

    final tools = skill.functionSchema != null
        ? [Tool.fromJsonSchema(skill.functionSchema!)]
        : null;

    final session = await _model!.createSession(
      systemInstruction: SystemInstruction.text(skill.systemInstruction),
      tools: tools,
    );

    try {
      await session.addQueryChunk(Message.text(
        text: skill.buildPrompt(SkillInput(text: input, extra: extra)),
        isUser: true,
      ));

      final stream = session.getResponseStream();
      FunctionCall? functionCall;

      await for (final event in stream) {
        if (event is FunctionCallResponse) {
          functionCall = event.functionCall;
          break;
        } else if (event is ErrorResponse) {
          throw Exception(event.message);
        }
      }

      if (functionCall != null) {
        return skill.parseResult(functionCall);
      }

      throw Exception('No function call returned');
    } finally {
      await session.close();
    }
  }

  /// Execute with streaming (for real-time token display)
  Stream<String> executeStream({
    required Skill skill,
    required String input,
  }) async* {
    final session = await _model!.createSession(
      systemInstruction: SystemInstruction.text(skill.systemInstruction),
    );

    await session.addQueryChunk(Message.text(text: input, isUser: true));

    final stream = session.getResponseStream();
    await for (final event in stream) {
      if (event is TextResponse) {
        yield event.text;
      }
    }
  }
}

Step 5: Skill Base Class

// skill.dart

import 'package:flutter/foundation.dart';
import 'package:flutter_gemma/flutter_gemma.dart';

abstract class Skill {
  String get id;
  String get name;
  String get description;
  String get systemInstruction;
  Map<String, dynamic>? get functionSchema => null;
  SkillResult parseResult(FunctionCall functionCall);
  String buildPrompt(SkillInput input);
}

class SkillInput {
  final String text;
  final List<Uint8List>? images;
  final Map<String, dynamic>? extra;

  const SkillInput({
    required this.text,
    this.images,
    this.extra,
  });
}

Usage Example

This follows Flutter best practices with Riverpod and feature-based structure.

Project Structure

lib/
├── main.dart
├── app.dart
├── features/
│   └── translation/
│       ├── data/
│       │   └── translation_repository.dart
│       ├── domain/
│       │   └── translation_service.dart
│       └── presentation/
│           ├── screens/
│           
Compartilhar X LinkedIn