Dart: Dart e Colaboração com Flutter

Última atualização: 2026-08-26

Dart é o coração do Flutter — uma única linguagem que impulsiona UI, lógica e comunicação nativa.

1. O que Você Aprenderá


2. A História Real de um Desenvolvedor

(1) O Problema: CLI e App Flutter Cada Um Implementando Lógica de Negócio Separada

O DataPipeline do Bob tem dois front-ends: uma ferramenta CLI (para desenvolvedores) e um dashboard Flutter (para clientes SaaS visualizarem relatórios). Os dois sistemas implementaram sua própria lógica de processamento de dados, levando a: corrigir um bug de cálculo de imposto exigia alterações em dois lugares; uma vez, uma alteração esquecida fez com que o relatório CLI estivesse correto mas o dashboard estivesse errado, resultando em reclamações de clientes.

(2) Solução: Pacote de Lógica Compartilhada

Extrair a lógica de negócio central em um pacote Dart puro, do qual tanto o CLI quanto o Flutter dependem. Corrigir um bug requer alteração em apenas um lugar.

100%
graph TD
  A[DataPipeline Core<br/>Dart puro] --> B[CLI App]
  A --> C[Flutter Dashboard]
  C --> D[MethodChannel]
  D --> E[iOS Nativo]
  D --> F[Android Nativo]
  C --> G[FFI]
  G --> H[Biblioteca C]
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.

(3) Benefícios


3. O Papel do Dart na Arquitetura Flutter

(1) Camadas da Arquitetura

Camada Tecnologia Responsabilidade
Camada UI Flutter Widget Renderização de interface e interação
Camada de Lógica Dart (puro) Lógica de negócio, processamento de dados
Camada de Plataforma Nativo (Kotlin/Swift) Funções específicas de plataforma
Camada de Comunicação MethodChannel / FFI Ponte Dart ↔ Nativo

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.

: Lógica Dart em um projeto Flutter

DART
// Este código roda em AMBOS os ambientes CLI e Flutter
// lib/data_pipeline_core.dart

class OrderAnalyzer {
  final double taxRate;

  OrderAnalyzer({this.taxRate = 0.08});

  double calculateTax(double amount) => amount * taxRate;
  double calculateTotal(double amount) => amount * (1 + taxRate);

  Map<String, double> groupByCategory(List``<Order>`` orders) {
    final result = <String, double>{};
    for (final order in orders) {
      result.update(order.category, (v) => v + order.amount, ifAbsent: () => order.amount);
    }
    return result;
  }
}

class Order {
  final String id;
  final double amount;
  final String category;

  Order({required this.id, required this.amount, required this.category});
}
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.

4. Design de Pacote de Lógica de Negócio Compartilhada

(1) Estrutura de Pacote Dart Puro

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.

: Estrutura do pacote compartilhado

TEXT 📖 Somente leitura
packages/
  data_pipeline_core/
    lib/
      src/
        models/
          order.dart
          product.dart
          customer.dart
        services/
          analyzer.dart
          aggregator.dart
          transformer.dart
        utils/
          formatters.dart
          validators.dart
      data_pipeline_core.dart  # Barrel export
    test/
      analyzer_test.dart
      aggregator_test.dart
    pubspec.yaml
```text


```text
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.

: Barrel export

DART
// lib/data_pipeline_core.dart
// Barrel file - exporta todas as APIs públicas

// Models
export 'src/models/order.dart';
export 'src/models/product.dart';
export 'src/models/customer.dart';

// Services
export 'src/services/analyzer.dart';
export 'src/services/aggregator.dart';
export 'src/services/transformer.dart';

// Utils
export 'src/utils/formatters.dart';
export 'src/utils/validators.dart';
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.

: pubspec.yaml para pacote Dart puro

YAML
name: data_pipeline_core
description: Core business logic for DataPipeline - pure Dart, no Flutter dependency
version: 1.0.0

environment:
  sdk: ^3.0.0

dependencies:
  json_annotation: ^4.8.0

dev_dependencies:
  test: ^1.24.0
  json_serializable: ^6.7.0
  build_runner: ^2.4.0

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.

: Dois consumidores

YAML
# CLI app pubspec.yaml
dependencies:
  data_pipeline_core:
    path: ../packages/data_pipeline_core
  args: ^2.4.2

# Flutter app pubspec.yaml
dependencies:
  data_pipeline_core:
    path: ../packages/data_pipeline_core
  flutter:
    sdk: flutter
Princípio Descrição
Sem dependência Flutter pubspec.yaml não depende de flutter
Sem uso direto de dart:io Usar interfaces abstratas para operações de arquivo/rede
Sem código UI Lógica de processamento de dados pura
Testes completos Testável sem o runtime Flutter

5. MethodChannel

(1) Comunicação Dart ↔ Nativo

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.

: MethodChannel chamando nativo

DART
import 'package:flutter/services.dart';

class NativeService {
  static const _channel = MethodChannel('com.datapipeline/native');

  // Chamar método nativo
  Future``<String>`` getDeviceId() async {
    try {
      final deviceId = await _channel.invokeMethod``<String>``('getDeviceId');
      return deviceId ?? 'unknown';
    } on PlatformException catch (e) {
      print('Failed to get device ID: ${e.message}');
      return 'error';
    }
  }

  // Chamar com argumentos
  Future``<bool>`` saveToFile(String path, String content) async {
    try {
      final result = await _channel.invokeMethod``<bool>``('saveToFile', {
        'path': path,
        'content': content,
      });
      return result ?? false;
    } on PlatformException catch (e) {
      print('Failed to save: ${e.message}');
      return false;
    }
  }
}
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.

(2) Implementação do Lado Nativo

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.

: Lado Android (Kotlin)

KOTLIN
// Android implementation
class MainActivity : FlutterActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger,
            "com.datapipeline/native").setMethodCallHandler { call, result ->
            when (call.method) {
                "getDeviceId" -> {
                    val deviceId = Settings.Secure.getString(
                        contentResolver, Settings.Secure.ANDROID_ID)
                    result.success(deviceId)
                }
                "saveToFile" -> {
                    val path = call.argument<String>("path")
                    val content = call.argument<String>("content")
                    // Save file logic
                    result.success(true)
                }
                else -> result.notImplemented()
            }
        }
    }
}
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.
Método de Comunicação Direção Tipo de Dados Caso de Uso
MethodChannel Dart → Nativo Tipos padrão Chamando métodos nativos
EventChannel Nativo → Dart Stream Fluxos de eventos nativos
BasicMessageChannel Bidirecional String/Bytes Mensagens bidirecionais

6. FFI — Chamando Bibliotecas C

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.

: dart:ffi chamando uma função C

DART
import 'dart:ffi';
import 'package:ffi/ffi.dart';

// Assinatura da função C: double process_data(double* data, int length)
typedef ProcessDataNative = Double Function(Pointer``<Double>``, Int32);
typedef ProcessDataDart = double Function(Pointer``<Double>``, int);

void main() {
  // Carregar biblioteca dinâmica
  final dylib = DynamicLibrary.open('libdatapipeline.so');

  // Procurar função
  final processData = dylib.lookupFunction<ProcessDataNative, ProcessDataDart>('process_data');

  // Preparar dados
  final dataPtr = calloc``<Double>``(5);
  for (var i = 0; i < 5; i++) {
    dataPtr[i] = (i + 1) * 100.0;
  }

  // Chamar função C
  final result = processData(dataPtr, 5);
  print('Result from C: $result');

  // Liberar memória
  calloc.free(dataPtr);
}
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.
Conceito FFI Descrição
DynamicLibrary Carregar bibliotecas de link dinâmico
lookupFunction Procurar uma função C
`Pointer````` Aponta para memória nativa
calloc / free Alocar/liberar memória nativa
NativeType Mapeamento de tipo C

7. Cenário do Bob: Arquitetura de Duplo Front-end do DataPipeline

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.

: Core compartilhado + duplo front-end

DART
// packages/data_pipeline_core/lib/src/services/analyzer.dart

class OrderAnalyzer {
  final double taxRate;

  OrderAnalyzer({this.taxRate = 0.08});

  AnalysisResult analyze(List``<Order>`` orders) {
    final completed = orders.where((o) => o.status == 'completed').toList();
    final revenue = completed.fold``<double>``(0, (s, o) => s + o.amount);
    final tax = revenue * taxRate;

    final byCategory = <String, double>{};
    for (final o in completed) {
      byCategory.update(o.category, (v) => v + o.amount, ifAbsent: () => o.amount);
    }

    return AnalysisResult(
      totalOrders: orders.length,
      completedOrders: completed.length,
      revenue: revenue,
      tax: tax,
      revenueByCategory: byCategory,
    );
  }
}

class AnalysisResult {
  final int totalOrders;
  final int completedOrders;
  final double revenue;
  final double tax;
  final Map<String, double> revenueByCategory;

  AnalysisResult({
    required this.totalOrders,
    required this.completedOrders,
    required this.revenue,
    required this.tax,
    required this.revenueByCategory,
  });

  double get totalWithTax => revenue + tax;
  double get averageOrderValue => completedOrders > 0 ? revenue / completedOrders : 0;
}

class Order {
  final String id;
  final double amount;
  final String status;
  final String category;

  Order({required this.id, required this.amount, required this.status, required this.category});
}

// Consumidor CLI
void main() {
  final analyzer = OrderAnalyzer(taxRate: 0.08);
  final orders = [
    Order(id: 'ORD-001', amount: 1500.0, status: 'completed', category: 'Electronics'),
    Order(id: 'ORD-002', amount: 890.0, status: 'completed', category: 'Clothing'),
  ];

  final result = analyzer.analyze(orders);
  print('Revenue: \$${result.revenue.toStringAsFixed(2)} USD');
  print('Tax: \$${result.tax.toStringAsFixed(2)} USD');
}

// Consumidor Flutter usaria o mesmo OrderAnalyzer
// mas renderizaria resultados em um Widget em vez de print()
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.

8. Exemplo Completo: Arquitetura Compartilhada do DataPipeline

DART
// ============================================
// DataPipeline Shared Architecture
// Core Dart puro + consumidor CLI + consumidor Flutter
// ============================================

// ---- Biblioteca Core (Dart puro) ----

// lib/src/models/order.dart
class Order {
  final String id;
  final double amount;
  final String status;
  final String category;
  final String region;

  const Order({
    required this.id,
    required this.amount,
    required this.status,
    required this.category,
    this.region = 'US',
  });

  double get tax => amount * _taxRate(region);
  double get total => amount + tax;
  String get formatAmount => '\$${amount.toStringAsFixed(2)} USD';

  static double _taxRate(String region) => switch (region) {
    'US' => 0.08,
    'EU' => 0.20,
    'UK' => 0.15,
    _ => 0.10,
  };
}

// lib/src/services/analyzer.dart
class OrderAnalyzer {
  const OrderAnalyzer();

  AnalysisResult analyze(List``<Order>`` orders) {
    final completed = orders.where((o) => o.status == 'completed').toList();
    final revenue = completed.fold``<double>``(0, (s, o) => s + o.amount);
    final tax = completed.fold``<double>``(0, (s, o) => s + o.tax);
    final total = revenue + tax;

    final byCategory = <String, CategoryResult>{};
    for (final o in completed) {
      byCategory.update(
        o.category,
        (v) => v.addOrder(o),
        ifAbsent: () => CategoryResult.fromOrder(o),
      );
    }

    return AnalysisResult(
      totalOrders: orders.length,
      completedOrders: completed.length,
      revenue: revenue,
      tax: tax,
      total: total,
      byCategory: byCategory,
    );
  }
}

class CategoryResult {
  final int count;
  final double revenue;

  const CategoryResult({required this.count, required this.revenue});

  factory CategoryResult.fromOrder(Order order) =>
      CategoryResult(count: 1, revenue: order.amount);

  CategoryResult addOrder(Order order) =>
      CategoryResult(count: count + 1, revenue: revenue + order.amount);

  double get average => count > 0 ? revenue / count : 0;
}

class AnalysisResult {
  final int totalOrders;
  final int completedOrders;
  final double revenue;
  final double tax;
  final double total;
  final Map<String, CategoryResult> byCategory;

  const AnalysisResult({
    required this.totalOrders,
    required this.completedOrders,
    required this.revenue,
    required this.tax,
    required this.total,
    required this.byCategory,
  });

  double get averageOrderValue => completedOrders > 0 ? revenue / completedOrders : 0;
}

// ---- Consumidor CLI ----
void main() {
  final analyzer = OrderAnalyzer();
  final orders = [
    Order(id: 'ORD-001', amount: 1500.0, status: 'completed', category: 'Electronics'),
    Order(id: 'ORD-002', amount: 890.0, status: 'completed', category: 'Clothing'),
    Order(id: 'ORD-003', amount: 3200.0, status: 'pending', category: 'Electronics'),
    Order(id: 'ORD-004', amount: 2100.0, status: 'completed', category: 'Electronics', region: 'EU'),
  ];

  final result = analyzer.analyze(orders);

  print('=== DataPipeline Analytics ===');
  print('Orders:    ${result.completedOrders}/${result.totalOrders}');
  print('Revenue:   \$${result.revenue.toStringAsFixed(2)} USD');
  print('Tax:       \$${result.tax.toStringAsFixed(2)} USD');
  print('Total:     \$${result.total.toStringAsFixed(2)} USD');
  print('Average:   \$${result.averageOrderValue.toStringAsFixed(2)} USD');

  print('\nBy Category:');
  final sorted = result.byCategory.entries.toList()
    ..sort((a, b) => b.value.revenue.compareTo(a.value.revenue));
  for (final entry in sorted) {
    final cat = entry.value;
    print('  ${entry.key}: ${cat.count} orders, '
        '\$${cat.revenue.toStringAsFixed(2)} USD (avg: \$${cat.average.toStringAsFixed(2)})');
  }

  print('\n--- Platform Compatibility ---');
  print('Core logic:  Pure Dart (CLI + Flutter + Web)');
  print('CLI:         dart:io + args package');
  print('Flutter:     Widgets + MethodChannel + FFI');
}
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente dependendo das versões do SDK.

Saída:

TEXT 📖 Somente leitura
=== DataPipeline Analytics ===
Orders:    3/4
Revenue:   $4490.00 USD
Tax:       $798.00 USD
Total:     $5288.00 USD
Average:   $1496.67 USD

By Category:
  Electronics: 2 orders, $3600.00 USD (avg: $1800.00)
  Clothing: 1 orders, $890.00 USD (avg: $890.00)

--- Platform Compatibility ---
Core logic:  Pure Dart (CLI + Flutter + Web)
CLI:         dart:io + args package
Flutter:     Widgets + MethodChannel + FFI

❓ Perguntas Frequentes

P: Um pacote Dart puro pode rodar no Flutter Web? R: Sim, desde que dart:io não seja usado. A plataforma web não suporta dart:io; use interfaces abstratas (como o Client do pacote http) para substituir operações diretas de arquivo/rede.

P: Há um limite de tamanho para transferência de dados via MethodChannel? R: Não há limite rígido, mas objetos grandes requerem serialização/desserialização, o que tem desempenho ruim. Para dados grandes, considere usar FFI ou arquivos temporários.

P: FFI suporta iOS? R: Sim. No iOS, carregue .dylib ou .framework. Flutter 3.0+ FFI suporta todas as principais plataformas.

P: Quando devo usar MethodChannel vs. FFI? R: MethodChannel é adequado para chamar APIs de plataforma (sensores, notificações push, salvamento de arquivos); FFI é adequado para chamar bibliotecas C (processamento de imagens, criptografia, motores de banco de dados).

P: Testar um pacote compartilhado requer ambiente Flutter? R: Não. Um pacote Dart puro pode ser testado apenas com dart test e não depende do Flutter. Este é um grande benefício de extrair um pacote compartilhado.

P: Como garantir que um pacote compartilhado não introduza acidentalmente dependências Flutter? R: Não dependa de flutter no pubspec.yaml, e não importe pacotes flutter. No CI, execute testes com dart test (não flutter test) para verificar.

P: O pacote core do DataPipeline deve usar um monorepo ou um repositório separado? R: Um monorepo é recomendado (usando Melos ou dependências de caminho) para facilitar a sincronização entre o pacote core e os consumidores. Um repositório separado é adequado para distribuição open-source.


📖 Resumo


📝 Exercícios

  1. Básico (Dificuldade ⭐): Crie um pacote Dart puro (dart create -t package-simple) contendo uma função formatUSD. Referencie e chame-a de ambos um projeto CLI e um projeto Flutter.
  2. Intermediário (Dificuldade ⭐⭐): Design a estrutura do pacote core do DataPipeline: 3 classes de modelo + 2 classes de serviço + 1 barrel export. Garanta que pubspec.yaml não tenha dependência Flutter, e escreva testes executáveis com dart test.
  3. Desafio (Dificuldade ⭐⭐⭐): Implemente uma interface abstrata DataStorage (Dart puro), depois implemente-a usando dart:io (CLI) e SharedPreferences (Flutter) separadamente. O pacote core deve depender apenas da interface, com implementações concretas injetadas pelos consumidores.

← Lição Anterior | Próxima Lição →

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%