Dart: Dart 与 Flutter 协作 — 共享逻辑与平台通信

Dart 是 Flutter 的心脏 — 同一套语言,驱动 UI、逻辑、原生通信。

1. 你将学到


2. 一个开发者的真实故事

(1) 痛点:CLI 和 Flutter App 各写一套业务逻辑

Bob 的 DataPipeline 有两个前端:CLI 工具(供开发者使用)和 Flutter 仪表盘(供 SaaS 客户查看报表)。两套系统各自实现数据处理逻辑,导致:修一个税额计算 bug 要改 2 处,一次漏改导致 CLI 报表正确但仪表盘错误,客户投诉。

(2) 共享逻辑包的解法

将核心业务逻辑抽取为 pure Dart package,CLI 和 Flutter 都依赖它。修 bug 只需改一处。

100%
graph TD
  A[DataPipeline Core<br/>pure Dart] --> B[CLI App]
  A --> C[Flutter Dashboard]
  C --> D[MethodChannel]
  D --> E[iOS Native]
  D --> F[Android Native]
  C --> G[FFI]
  G --> H[C Library]
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

(3) 收益


3. Dart 在 Flutter 架构中的定位

(1) 架构分层

层级 技术 职责
UI 层 Flutter Widget 界面渲染与交互
逻辑层 Dart (pure) 业务逻辑、数据处理
平台层 Native (Kotlin/Swift) 平台特定功能
通信层 MethodChannel / FFI Dart ↔ Native 桥梁

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:Flutter 项目中的 Dart 逻辑

DART
// This code runs in BOTH CLI and Flutter environments
// 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 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

4. 共享业务逻辑包设计

(1) Pure Dart Package 结构

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:共享包结构

TEXT 📖 仅展示
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
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:Barrel export

DART
// lib/data_pipeline_core.dart
// Barrel file - exports all public APIs

// 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 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:pubspec.yaml for pure Dart package

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

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:两个消费者

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
原则 说明
无 Flutter 依赖 pubspec.yaml 不依赖 flutter
无 dart:io 直接使用 用抽象接口替代文件/网络操作
无 UI 代码 纯数据处理逻辑
完整测试 不需要 Flutter 运行时即可测试

5. MethodChannel

(1) Dart ↔ Native 通信

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:MethodChannel 调用原生

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

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

  // Call native method
  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';
    }
  }

  // Call with arguments
  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 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

(2) Native 端实现

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

: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 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
通信方式 方向 数据类型 适用场景
MethodChannel Dart → Native 标准类型 调用原生方法
EventChannel Native → Dart 原生事件流
BasicMessageChannel 双向 字符串/字节 双向消息

6. FFI — 调用 C 库

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:dart:ffi 调用 C 函数

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

// C function signature: double process_data(double* data, int length)
typedef ProcessDataNative = Double Function(Pointer``<Double>``, Int32);
typedef ProcessDataDart = double Function(Pointer``<Double>``, int);

void main() {
  // Load dynamic library
  final dylib = DynamicLibrary.open('libdatapipeline.so');

  // Look up function
  final processData = dylib.lookupFunction<ProcessDataNative, ProcessDataDart>('process_data');

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

  // Call C function
  final result = processData(dataPtr, 5);
  print('Result from C: $result');

  // Free memory
  calloc.free(dataPtr);
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
FFI 概念 说明
DynamicLibrary 加载动态链接库
lookupFunction 查找 C 函数
`Pointer````` 指向原生内存
calloc / free 分配/释放原生内存
NativeType C 类型映射

7. Bob 场景:DataPipeline 双前端架构

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:共享核心 + 双前端

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});
}

// CLI consumer
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');
}

// Flutter consumer would use the same OrderAnalyzer
// but render results in a Widget instead of print()
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

8. 完整示例:DataPipeline 共享架构

DART
// ============================================
// DataPipeline Shared Architecture
// Pure Dart core + CLI consumer + Flutter consumer
// ============================================

// ---- Core Library (pure Dart) ----

// 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;
}

// ---- CLI Consumer ----
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 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

输出:

TEXT 📖 仅展示
=== 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

❓ 常见问题

Q:pure Dart package 能在 Flutter Web 上运行吗? A:可以,只要不使用 dart:io。Web 平台不支持 dart:io,使用抽象接口(如 http 包的 Client)替代直接文件/网络操作。

Q:MethodChannel 的数据传递有大小限制吗? A:没有硬性限制,但大对象需要序列化/反序列化,性能不佳。大数据建议用 FFI 或临时文件传递。

Q:FFI 支持 iOS 吗? A:支持。iOS 上加载 .dylib 或 .framework。Flutter 3.0+ 的 FFI 支持所有主要平台。

Q:什么时候用 MethodChannel,什么时候用 FFI? A:MethodChannel 适合调用平台 API(传感器、推送、文件保存);FFI 适合调用 C 库(图像处理、加密、数据库引擎)。

Q:共享包的测试需要在 Flutter 环境吗? A:不需要。pure Dart package 用 dart test 即可测试,不依赖 Flutter。这是抽取共享包的重要好处。

Q:如何确保共享包不意外引入 Flutter 依赖? A:pubspec.yaml 不依赖 flutter,不 import flutter 的包。CI 中用 dart test(不是 flutter test)运行测试来验证。

Q:DataPipeline 核心包应该用 monorepo 还是独立仓库? A:推荐 monorepo(Melos 或 path 依赖),方便同步修改核心包和消费者。独立仓库适合开源发布。


📖 小节


📝 作业

  1. 基础题(难度⭐):创建一个 pure Dart package(dart create -t package-simple),包含一个 formatUSD 函数,分别在 CLI 和 Flutter 项目中引用并调用它。
  2. 进阶题(难度⭐⭐):设计 DataPipeline 的核心包结构:3 个模型类 + 2 个服务类 + 1 个 barrel export。确保 pubspec.yaml 无 Flutter 依赖,编写 dart test 可运行的测试。
  3. 挑战题(难度⭐⭐⭐):实现一个抽象的 DataStorage 接口(pure Dart),分别用 dart:io(CLI)和 SharedPreferences(Flutter)实现。核心包只依赖接口,具体实现由消费者注入。

← 上一课 | 下一课 →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏